You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by jt...@apache.org on 2017/09/03 12:48:34 UTC

[01/24] incubator-netbeans-html4j git commit: [INFRA-15006] Initial donation of HTML/Java NetBeans API. Equivalent to the content of html4j-donation-review.zip donated as part of ApacheNetBeansDonation1.zip with SHA256 being 7f2ca0f61953a190613c9a0fbcc1b

Repository: incubator-netbeans-html4j
Updated Branches:
  refs/heads/master [created] 226089a5a


http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/src/main/javadoc/overview.html
----------------------------------------------------------------------
diff --git a/src/main/javadoc/overview.html b/src/main/javadoc/overview.html
new file mode 100644
index 0000000..850c0d7
--- /dev/null
+++ b/src/main/javadoc/overview.html
@@ -0,0 +1,623 @@
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>HTML for Java APIs</title>
+        <meta charset="UTF-8">
+        <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    </head>
+    <body>
+        <p>
+         Use Java to write application logic; Use HTML5 to render the UI;
+         {@link net.java.html.json.Model Animate an HTML page from Java}
+         (see <a target="_blank" href="http://dew.apidesign.org/dew/#7212206">Duke being rotated</a> by CSS);
+         Use {@link net.java.html.json.OnReceive REST} or
+         <a href="net/java/html/json/doc-files/websockets.html">WebSockets</a>;
+         interact with <a href="net/java/html/js/package-summary.html">JavaScript</a>;
+         Get the best of both worlds!
+
+         The goal of these APIs is to use full featured Java runtime
+         (like real <a target="_blank" href="http://wiki.apidesign.org/wiki/HotSpot">HotSpot VM</a>),
+         but still rely on a very lightweight rendering technology
+         (so it can potentially fit
+         <a target="_blank" href="http://bck2brwsr.apidesign.org">Bck2Brwsr</a> and definitely
+         to various types of phones). What can be more lightweight
+         (from a browser perspective) than
+         <a target="_blank" href="http://wiki.apidesign.org/wiki/HTML">HTML</a>!?
+         By default we use {@link net.java.html.boot.fx JavaFX's WebView}
+         component to display the <a target="_blank" href="http://wiki.apidesign.org/wiki/HTML">HTML</a>.
+         We eliminate the need to manipulate the DOM directly,
+         there is a special {@link net.java.html.json Java to Knockout.js binding}.
+         As a result the <a target="_blank" href="http://knockoutjs.com">HTML uses Knockout.js syntax</a>,
+         yet the application code can be written in Java.
+        </p>
+
+        <h3>New features in version 1.4</h3>
+
+        Both values <code>null</code> and <code>undefined</code> are
+        <a href="net/java/html/js/package-summary.html#undefined">treated as null</a>.
+        Better behavior under <a target="_blank" href="https://netbeans.org/bugzilla/show_bug.cgi?id=259132">
+        multi-threaded load</a>.
+        Integration with <a href="net/java/html/boot/truffle/package-summary.html">Truffle</a>.
+
+        <h3>Improvements in version 1.3</h3>
+
+        {@link net.java.html.json.Model Model classes} can have 
+        {@link net.java.html.json.Model#instance() per-instance private data}.
+        {@link net.java.html.json.Model Model classes} can generate
+        builder-like construction methods if builder
+        {@link net.java.html.json.Model#builder() prefix} is specified.
+        {@link net.java.html.json.Property#mutable} can be <code>false</code>
+        to define a non-mutable (almost constant) property. That
+        in case of <em>Knockout</em> bindings means: the property is
+        represented by a plain value rather than an observable in the JavaScript
+        object. The <em>JavaFX</em> presenter can be executed in headless mode -
+        just specify <code>-Dfxpresenter.headless=true</code> when launching
+        its virtual machine and no window will be shown. This is particularly
+        useful for testing. Configure your <em>surefire</em> or <em>failsafe</em>
+        plugins like: <pre>
+&lt;plugin&gt;
+  &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
+  &lt;artifactId&gt;maven-surefire-plugin&lt;/artifactId&gt;
+  &lt;version&gt;2.13&lt;/version&gt;
+  &lt;configuration&gt;
+      &lt;systemPropertyVariables&gt;
+          &lt;fxpresenter.headless&gt;true&lt;/fxpresenter.headless&gt;
+      &lt;/systemPropertyVariables&gt;
+  &lt;/configuration&gt;
+&lt;/plugin&gt;
+</pre>
+        OSGi headers are now <a target="_blank" href="https://netbeans.org/bugzilla/show_bug.cgi?id=256696">
+            enterprise OSGi ready</a>.
+        Switched to <a target="_blank" href="https://netbeans.org/bugzilla/show_bug.cgi?id=257130">minified version 3.4.0</a>
+        of <a target="_blank" href="http://knockoutjs.com">knockout.js</a>.
+        Better support for <a target="_blank" href="https://netbeans.org/bugzilla/show_bug.cgi?id=257348">
+        recursive @Model definitions</a>.
+        New module <code>org.netbeans.html:xhr4j</code> provides implementation
+        of {@link org.netbeans.html.json.spi.Transfer} with
+        {@link org.netbeans.html.context.spi.Contexts.Id technology identifier}
+        <b>xhr4j</b> - this module can be used to
+        <a target="_blank" href='https://netbeans.org/bugzilla/show_bug.cgi?id=257849'>workaround limitations
+        of CORS</a> by handling the {@link net.java.html.json.OnReceive}
+        connections in Java.
+
+        <h3>What's Been Improved in Version 1.2.3?</h3>
+
+        One can control {@link net.java.html.json.OnReceive#headers() HTTP request headers}
+        when connecting to server using the {@link net.java.html.json.OnReceive}
+        annotation. It is possible to have
+        {@link net.java.html.json.ComputedProperty#write() writable computed properties}.
+        There is an easy way to enable <a target="_blank" href="http://getfirebug.com/">Firebug</a> in
+        the JavaFX based Web View -
+        just run with <code>-Dfirebug.lite=true</code> as
+        <a target="_blank" href="https://www.youtube.com/watch?v=2rxwY-QJiLo">this video</a>
+        demonstrates.
+        Bugfix of issues <a target="_blank" href='https://netbeans.org/bugzilla/show_bug.cgi?id=250503'>250503</a>,
+        <a target="_blank" href='https://netbeans.org/bugzilla/show_bug.cgi?id=252987'>252987</a>.
+
+        <h3>What's New in Version 1.1?</h3>
+
+        <p>
+            The content of a {@link net.java.html.BrwsrCtx context}
+            can be selected by registering implementations under specific
+            {@link org.netbeans.html.context.spi.Contexts.Id technology identifiers}
+            and requesting them during
+            {@link org.netbeans.html.context.spi.Contexts#newBuilder(java.lang.Object...) construction}
+            of the context. <code>org.netbeans.html:ko4j</code> module's implementation
+            offers <b>ko4j</b>, <b>xhr</b> and <b>websocket</b> identifiers
+            for its registered services
+            (e.g. {@link org.netbeans.html.json.spi.Technology},
+            {@link org.netbeans.html.json.spi.Transfer} and
+            {@link org.netbeans.html.json.spi.WSTransfer}).
+            <code>org.netbeans.html:ko-ws-tyrus</code>
+            module registers its
+            {@link org.netbeans.html.json.spi.Transfer Java based JSON} and
+            {@link org.netbeans.html.json.spi.WSTransfer WebSocket} implementations
+            under the name <b>tyrus</b>.
+        </p>
+        <p>
+            A particular DOM subtree
+            that a <a target="_blank" href="http://knockoutjs.com">knockout.js</a> model gets
+            applied to can be selected by using
+            {@link net.java.html.json.Models#applyBindings(java.lang.Object,java.lang.String)
+            Models.applyBindings(m, id)} with an id of an HTML element.
+            There is new {@link net.java.html.json.Model#targetId()} attribute
+            which controls behavior of the generated <code>applyBindings</code> method.
+            If <em>specified and non-empty</em>, then the generated method
+            will call {@link net.java.html.json.Models#applyBindings(java.lang.Object,java.lang.String)}
+            with <code>this</code> and the provided {@link net.java.html.json.Model#targetId() target id}.
+            If <em>specified, but left empty</em>, then the generated method
+            calls {@link net.java.html.json.Models#applyBindings(java.lang.Object)}.
+            <em>If unspecified</em>, the method will <b>not</b> be generated at all
+            (a change with respect to older versions). However one can
+            still use {@link net.java.html.json.Models#applyBindings(java.lang.Object)}
+            or {@link net.java.html.json.Models#applyBindings(java.lang.Object,java.lang.String)}
+            to perform the association of any model with the page element.
+        </p>
+        <p>
+            Memory model when using Knockout bindings has been improved
+            (required additions of two new methods:
+            {@link org.netbeans.html.json.spi.PropertyBinding#weak()} and
+            {@link org.netbeans.html.json.spi.FunctionBinding#weak()}) and
+            now the Java {@link net.java.html.json.Model models} can garbage collect,
+            when no longer used. Library writers that use
+            {@link net.java.html.js.JavaScriptBody} annotation can also
+            control garbage collection behavior of method arguments by
+            setting {@link net.java.html.js.JavaScriptBody#keepAlive() keepAlive=false}
+            attribute.
+        </p>
+
+        <h3>What's New in Version 1.0?</h3>
+
+        <p>
+            {@link net.java.html.json.Property#array() Array properties} are now
+            mutable from the <a href="http://knockoutjs.com">knockout.js</a>
+            point of view (required {@link org.netbeans.html.json.spi.Proto.Type#replaceValue one SPI change}).
+            The page lookup mechanism can use {@link net.java.html.boot.BrowserBuilder#locale(java.util.Locale) locale}
+            to load localized a page with appropriate suffix.
+            All SPI were moved under the NetBeans namespace - e.g.
+            {@link org.netbeans.html.boot.spi},
+            {@link org.netbeans.html.context.spi},
+            {@link org.netbeans.html.json.spi},
+            {@link org.netbeans.html.sound.spi}, and also
+            {@link org.netbeans.html.json.tck}. Methods annotated
+            with {@link net.java.html.js.JavaScriptBody} annotation and
+            without fallback Java code now throw {@link java.lang.IllegalStateException}
+            with a message suggesting to switch to proper
+            {@link net.java.html.BrwsrCtx#execute browser context} to
+            prevent endless debugging when one forgets to do so.
+        </p>
+
+        <p>
+        What's new in older versions? Click the
+        <a href="#" onclick="return showHistoric(true)">link</a>
+        to view even more
+        <a href="#" onclick="return showHistoric(true)">historic changes</a> below:
+        </p>
+
+        <a name="historic.changes"></a>
+        <div id="historic.changes">
+            <script>
+            function showHistoric(show) {
+                var e = document.getElementById("historic.changes");
+                if (show) {
+                    e.style.display="block";
+                } else {
+                    e.style.display="none";
+                }
+                return false;
+            }
+            showHistoric(false);
+            </script>
+
+        <h3>What's New in Version 0.9?</h3>
+
+        <p>
+            System can run in {@link net.java.html.boot.BrowserBuilder#classloader(java.lang.ClassLoader) Felix OSGi container} (originally only Equinox).
+            {@link net.java.html.json.ComputedProperty Derived properties}
+            now deeply check changes in other {@link net.java.html.json.Model model
+            classes} they depend on and recompute their values accordingly.
+            <a target="_blank" href="http://knockoutjs.com">Knockout.js</a> library has been updated
+            to version 3.2.0.
+        </p>
+
+        <h3>What's New in 0.8.x Versions?</h3>
+
+        <p>
+            Setters or array properties on classes generated by {@link net.java.html.json.Model}
+            annotation can be accessed from any thread. {@link org.netbeans.html.sound.spi.AudioEnvironment}
+            can be registered into {@link net.java.html.BrwsrCtx}. There is
+            a {@link net.java.html.json.Models#parse(net.java.html.BrwsrCtx, java.lang.Class, java.io.InputStream, java.util.Collection)  method}
+            to parse a JSON array and convert it into
+            {@link net.java.html.json.Model model classes}.
+            Improved behavior of <code>enum</code> values in
+            {@link net.java.html.json.Model knockout bindings}.
+        </p>
+
+        <p>
+            Few bugfixes for better portability.
+            New API for {@link net.java.html.boot.script.Scripts headless execution}
+            on top of <em>Nashorn</em> - does not run <em>knockout for Java</em>
+            fully yet
+            (reported as <a href="https://bugs.openjdk.java.net/browse/JDK-8046013">JDK-8046013</a>),
+            however even in current state it is quite
+            {@link net.java.html.boot.script.Scripts useful for testing}
+            of
+            {@link net.java.html.js Java/JavaScript interactions}.
+        </p>
+
+        <p>
+            {@link net.java.html.boot.fx.FXBrowsers} has been extended
+            with new helper methods to make it easier to use HTML+Java
+            API in existing JavaFX applications.
+            The annotation processor is made
+            more robust with respect to errors in callback syntax of
+            {@link net.java.html.js.JavaScriptBody} body parameter.
+            Javadoc of {@link net.java.html.BrwsrCtx#execute} method
+            has been improved based on a failure of its usability study.
+            There can be additional parameters to methods annotated by
+            {@link net.java.html.json.OnReceive} that allows one to
+            pass state when a JSON call is made and use it when it finishes.
+            The mechanism of discovery of sibling HTML page has been
+            extended to work on systems that don't support
+            {@link java.lang.Class#getProtectionDomain}.
+        </p>
+
+        <p>
+            The first argument of method annotated by
+            {@link net.java.html.json.OnReceive} annotation has to
+            be the associated {@link net.java.html.json.Model model class}.
+        </p>
+
+        <p>
+            {@link net.java.html.json.OnReceive} annotation now accepts
+            {@link java.util.List} of data values as second argument
+            (previously required an array).
+        </p>
+
+
+        <h3>What's New in 0.7.x Versions?</h3>
+
+        <p>
+            {@link net.java.html.js.JavaScriptBody} annotation has new attribute
+            {@link net.java.html.js.JavaScriptBody#wait4js()} which allows
+            asynchronous execution. Libraries using
+            {@link net.java.html.js.JavaScriptBody} are urged to use this
+            new attribute as much as possible, as it can speed up execution
+            in certain environments.
+        </p>
+
+        <p>
+            Use {@link net.java.html.BrwsrCtx#execute(java.lang.Runnable)} in
+            multi-threaded environment to execute your code on the browser thread.
+            See example
+            {@link net.java.html.BrwsrCtx#execute(java.lang.Runnable) using Java timer}.
+        </p>
+        </div>
+
+        <h3>Interesting Entry Points</h3>
+
+        <p>Learn how to {@link net.java.html.json.Model animate an HTML page from Java}
+            without referencing single HTML element from the Java code.
+        </p>
+        <p>Use {@link net.java.html.json.OnReceive JSON} to communicate
+            with REST based server API.
+        </p>
+        <p>Use <a href="net/java/html/json/doc-files/websockets.html">WebSockets</a>
+            and JSON.
+        </p>
+        <p>Call JavaScript methods from Java and vice versa, via
+            <a href="net/java/html/js/package-summary.html">JavaScriptBody</a>.
+        </p>
+
+        <h3>Getting Started</h3>
+
+        This <b>HTML/Java</b> API is used by
+        <a target="_blank" href="https://platform.netbeans.org/tutorials/nbm-dukescript.html">
+        NetBeans Platform</a> as well as other project. The smoothest way
+        to get started is to follow the
+        <a target="_blank" href="https://dukescript.com/getting_started.html">getting started</a>
+        tutorial. In case one wants to stick with a <em>Maven</em> and command
+        line, one can follow
+        <a target="_blank" href="https://dukescript.com/update/2015/02/05/New-Version-of-Dukescript.html">
+            these instructions<a>, make sure at least
+        <em>JDK7</em> is your installed Java and type:
+        <pre>
+$ mvn archetype:generate \
+ -DarchetypeGroupId=com.dukescript.archetype \
+ -DarchetypeArtifactId=knockout4j-archetype \
+ -DarchetypeVersion=0.11 <em># or newer version, if available</em>
+        </pre>
+        Answer few questions (for example choose <em>myfirstbrwsrpage</em> as artifactId)
+        and then you can:
+        <pre>
+$ cd myfirstbrwsrpage
+$ mvn install
+$ mvn -f client/pom.xml process-classes exec:exec
+        </pre>
+        In a few seconds (or minutes if
+        <a target="_blank" href="http://wiki.apidesign.org/wiki/Maven">Maven</a>
+        decides to download the whole Internet of dependencies) you should
+        see a sample Hello World application.
+        Immediatelly you can be
+        <a target="_blank" href="https://dukescript.com/best/practices/2015/04/12/no-redeploys.html">
+        productive without any redeploys</a> - even more productive than
+        with plain JavaScript!
+        <p>
+        The application is rendered in a
+        <a target="_blank" href="http://wiki.apidesign.org/wiki/JavaFX">JavaFX</a>
+        web view component (that of course requires your JDK to come
+        with <a target="_blank" href="http://wiki.apidesign.org/wiki/JavaFX">JavaFX</a>;
+        <a target="_blank" href="http://www.oracle.com/technetwork/java/javase/downloads/index.html">JDK7
+            and JDK8 from Oracle</a> contain everything that is needed).
+        The generated application is built around one
+        Java source (uses the {@link net.java.html.json.Model} annotation to
+        auto-generate another <code>Data.java</code> class during compilation)
+        and one HTML file (uses the <a target="_blank" href="http://knockoutjs.com">Knockout</a>
+        syntax to <code>data-bind</code> the HTML elements to the
+        generated <code>Data</code> model):
+        <pre>
+$ ls client/src/main/java/**/DataModel.java
+$ ls client/src/main/webapp/pages/index.html
+        </pre>
+        That is all you need to get started. Play with the sources,
+        modify them and enjoy
+        <a target="_blank" href="http://html.java.net">Html for Java</a>!
+
+        <h2>IDE Support</h2>
+
+        <p>
+            This API is part of <a target="_blank"
+            href="http://netbeans.org">NetBeans.org</a> project and as such
+            it works naturally with the <a target="_blank"
+            href="https://netbeans.org/features/index.html">NetBeans IDE</a>.
+            On the other hand, the API is using nothing NetBeans specific,
+            it builds on standard Java6 APIs and as such it shall work fine
+            in any IDE.
+        </p>
+
+        <p>
+            A lot of work is done by
+            <a href="http://wiki.apidesign.org/wiki/AnnotationProcessor">
+            annotation processors</a>
+            that generate various boiler plate code during compilation. This
+            is a standard part of Java since JDK6, but for example Eclipse
+            is known not to deal with processors well and developers using
+            it need to be careful. IntelliJ users hasn't reported any issues
+            and of course, NetBeans IDE support for
+            <a href="http://wiki.apidesign.org/wiki/AnnotationProcessor">processors</a>
+            is outstanding.
+        </p>
+
+        <p>
+            When using {@link net.java.html.js.JavaScriptBody} annotation, it is
+            useful to do a bit of post processing of classes. There is a
+            <a href="http://wiki.apidesign.org/wiki/Maven">Maven</a>
+            plugin for that.
+            NetBeans IDE will invoke it when doing a build. Other IDEs may
+            need some hint to do so.
+            Anyway: If one does not see all (generated) sources or is getting
+            {@link java.lang.LinkageError}s when executing the application,
+            switch to command line and do clean build
+            from there:
+        </p>
+        <pre>$ mvn clean install</pre>
+        <p>
+            If that succeeds, your IDE of choice will hopefully
+            pick the generated sources up and present the result of the build
+            properly. If not,
+            <a href="https://netbeans.org/downloads/">download NetBeans</a>,
+            you will be pleasantly
+            surprised - for example with our excellent
+            <a href="net/java/html/js/package-summary.html#debugging">Java/JavaScript
+            debugging</a> support.
+        </p>
+
+        <a name="deploy">
+        <h2>Deploy Your Application</h2>
+        </a>
+
+        <p>
+        It is not goal of this documentation to list all possible ways
+        to package and deploy applications which use this API. However it is
+        important for new comers to see the benefits of using the
+        <a href="http://html.java.net">HTML for Java</a> API and as such
+        let's list at least few bundling options, known to work at the time of writing
+        this documentation.
+        </p>
+
+        <p>
+        First of all, this is a <em>client technology</em>. You write client applications
+        with it which may, but need not connect to a server. You don't need
+        Tomcat or WebLogic to deploy
+        <a href="http://html.java.net">HTML for Java</a> applications.
+        </p>
+
+        <p>
+            <img src='https://rawgit.com/jtulach/minesweeper/877b7983b1c1a157ebb85850896bcf04d7f5c7f2/client-web/src/main/webapp/resources/javafx_logo.jpg' width="64"
+                 height="64" align="left"/>
+            The sample project generated by
+            <code>org.apidesign.html knockout4j-archetype</code> is configured
+            to use <a href="http://wiki.apidesign.org/wiki/JavaFX">JavaFX</a>
+            as the rendering technology. This setup is primarily suitable for
+            development - it needs no special packaging, starts quickly and
+            allows you to use classical HotSpot VM debuggers. A final
+            artifact from the build is also a ZIP file which you can use
+            and distribute to your users. Good for desktop applications.
+        </p>
+
+        <p>
+            <img src='https://rawgit.com/jtulach/minesweeper/877b7983b1c1a157ebb85850896bcf04d7f5c7f2/client-web/src/main/webapp/resources/netbeans_logo.jpg' width="64"
+                 height="64" align="right"/>
+            <img src='https://rawgit.com/jtulach/minesweeper/877b7983b1c1a157ebb85850896bcf04d7f5c7f2/client-web/src/main/webapp/resources/eclipse_logo.png' width="64"
+                 height="64" align="right"/>
+            All the <a href="http://html.java.net">HTML for Java</a> libraries
+            are packaged as <a href="http://wiki.apidesign.org/wiki/OSGi">OSGi</a>
+            bundles and as such they can easily be run in NetBeans as well as
+            in Eclipse. As a result one can use
+            <a href="http://wiki.apidesign.org/wiki/OSGi">OSGi</a>
+            and have a common module system for both platforms. In addition to that
+            one can render using
+            HTML and have a common UI in both platforms. In such case
+            your application would be packaged as a set of
+            <a href="http://wiki.apidesign.org/wiki/OSGi">OSGi</a> bundles.
+            Read
+            <a href="http://wiki.apidesign.org/wiki/HTML">more</a>...
+        </p>
+
+        <p>
+            <img src='https://rawgit.com/jtulach/minesweeper/877b7983b1c1a157ebb85850896bcf04d7f5c7f2/client-web/src/main/webapp/resources/chrome_logo.png' width="64"
+                 height="64" align="left"/>
+            <img src='https://rawgit.com/jtulach/minesweeper/877b7983b1c1a157ebb85850896bcf04d7f5c7f2/client-web/src/main/webapp/resources/safari_logo.png' width="64"
+                 height="64" align="left"/>
+            <img src='https://rawgit.com/jtulach/minesweeper/877b7983b1c1a157ebb85850896bcf04d7f5c7f2/client-web/src/main/webapp/resources/ie_logo.png' width="64"
+                 height="64" align="left"/>
+            <img src='https://rawgit.com/jtulach/minesweeper/877b7983b1c1a157ebb85850896bcf04d7f5c7f2/client-web/src/main/webapp/resources/firefox_logo.png' width="64"
+                 height="64" align="left"/>
+
+            There is more and more attempts to execute Java bytecode
+            in a browser, without any special Java plugin installed.
+            The <a href="http://html.java.net">HTML for Java</a> is
+            carefully designed to produce lightweight, well performing
+            applications even on such restricted environments. It uses
+            no reflection calls and that allows to statically pre-compile
+            the applications into JavaScript. One of such environments
+            is called <a target="_blank" href="http://wiki.apidesign.org/wiki/Bck2Brwsr">Bck2Brwsr</a>,
+            another <a target="_blank" href="http://wiki.apidesign.org/wiki/TeaVM">TeaVM</a>. Both support the
+            {@link net.java.html.js.JavaScriptBody} annotation. Read
+            <a target="_blank" href="http://wiki.apidesign.org/wiki/Bck2BrwsrViaCLI">more</a> or play
+            a minesweeper game packaged for your browser
+            (of course <a target="_blank"
+                href="https://github.com/jtulach/minesweeper">written</a> in Java):
+        </p>
+
+        <script type="text/html" id="field">
+            <style type="text/css">
+                table.field td {
+                    padding: 4px;
+                    width: 18px;
+                    height: 18px;
+                    font-size: 1.5em;
+                    border: 1px solid black;
+                }
+                table.field td.UNKNOWN {
+                    background-color: #D6E4E1;
+                    cursor: pointer;
+                }
+                table.field td.EXPLOSION {
+                    background-color: #A31E39;
+                }
+                table.field td.DISCOVERED {
+                    background-color: #9DB2B1;
+                }
+            </style>
+            <table class="field">
+                <tbody>
+                    <!-- ko foreach: rows -->
+                    <tr>
+                        <!-- ko foreach: columns -->
+                        <td data-bind="css: style, click: $parents[1].click" >
+                            <div data-bind='html: html'></div>
+                        </td>
+                        <!-- /ko -->
+                    </tr>
+                    <!-- /ko -->
+                </tbody>
+            </table>
+        </script>
+
+        <center>
+        <div id="minesweeper" style="background-color: #f0f0f0; align: center">
+        <div data-bind="template: { name : 'field', if: fieldShowing }"></div>
+        </div>
+        </center>
+
+        <!-- boot bck2brwsr -->
+        <script type="text/javascript" src="https://rawgit.com/jtulach/minesweeper/877b7983b1c1a157ebb85850896bcf04d7f5c7f2/client-web/src/main/webapp/resources/teavm.js"></script>
+        <script>
+            var vm = new VM();
+            vm.loadClass('org.apidesign.demo.minesweeper.MainBrwsr');
+        </script>
+
+        <p>
+            <img src='https://rawgit.com/jtulach/minesweeper/877b7983b1c1a157ebb85850896bcf04d7f5c7f2/client-web/src/main/webapp/resources/ios_logo.jpg' width="64"
+                 height="64" align="right"/>
+            <img src='https://rawgit.com/jtulach/minesweeper/877b7983b1c1a157ebb85850896bcf04d7f5c7f2/client-web/src/main/webapp/resources/android_logo.jpg' width="64"
+                 height="64" align="right"/>
+
+            Now when we have seen that the
+            <a href="http://html.java.net">HTML for Java</a> applications
+            can run on any modern browser, we can ask whether they can also
+            fit into a phone!? Yes, they can and especially to phones
+            that can execute Java code already! Just by changing your
+            packaging you can create an APK file and deploy it to your
+            Android phone.
+            Read <a target="_blank" href="http://wiki.apidesign.org/wiki/DlvkBrwsr">more</a>
+            or install <a target="_blank" href="https://play.google.com/store/apps/details?id=org.apidesign.demo.minesweeper">
+                Fair Minesweeper for Android</a>...
+        </p>
+        <p>
+            In case you'd like your application to reach out to second biggest
+            group of smartphone users, don't despair: It
+            seems the set of devices that can execute
+            <a href="http://html.java.net">HTML for Java</a> applications
+            has been extended to <em>iPads</em> and <em>iPhones</em>. Get the
+            <a target="_blank" href="http://wiki.apidesign.org/wiki/IBrwsr">details here</a>
+            and play <a target="_blank" href="https://itunes.apple.com/us/app/fair-minesweeper/id903688146">
+                Fair Minesweeper on iOS</a>!
+        </p>
+        <p>
+            Convinced it makes sense to use
+            <a href="http://html.java.net">HTML for Java</a>
+            APIs for writing applications that are
+            <em>written once, displayed anywhere</em>? Or do you have an
+            environment which is not supported? In such case you can bring
+            <a href="http://html.java.net">HTML for Java</a>
+            to your environment yourself. Just implement your own
+            {@link org.netbeans.html.boot.spi.Fn.Presenter}!
+        </p>
+
+        <h2>Other Resources</h2>
+
+        <img src="net/java/html/json/doc-files/DukeHTML.png" width="256" height="184" alt="Duke and HTML5. Together at last!" align="right"/>
+
+        The javadoc for latest and previous versions is also available
+        online:
+        <ul>
+            <li>Current <a target="_blank" href="http://bits.netbeans.org/html+java/dev/">development</a> version
+            <li>Version <a target="_blank" href="http://bits.netbeans.org/html+java/1.2.3">1.2.3</a>
+            <li>Version <a target="_blank" href="http://bits.netbeans.org/html+java/1.1">1.1</a>
+            <li>Version <a target="_blank" href="http://bits.netbeans.org/html+java/1.0">1.0</a>
+            <li>Version <a target="_blank" href="http://bits.netbeans.org/html+java/0.9">0.9</a>
+                and historic ones (<a target="_blank" href="http://bits.netbeans.org/html+java/0.8.3">0.8.3</a>,
+                <a target="_blank" href="http://bits.netbeans.org/html+java/0.8.2">0.8.2</a>,
+                <a target="_blank" href="http://bits.netbeans.org/html+java/0.8.1">0.8.1</a>,
+                <a target="_blank" href="http://bits.netbeans.org/html+java/0.8">0.8</a>, and
+                <a target="_blank" href="http://bits.netbeans.org/html+java/0.7.5">0.7.5</a>)
+            </li>
+        </ul>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/xhr4j/pom.xml
----------------------------------------------------------------------
diff --git a/xhr4j/pom.xml b/xhr4j/pom.xml
new file mode 100644
index 0000000..947ef26
--- /dev/null
+++ b/xhr4j/pom.xml
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2016 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<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">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.netbeans.html</groupId>
+    <artifactId>pom</artifactId>
+    <version>2.0-SNAPSHOT</version>
+  </parent>
+  <groupId>org.netbeans.html</groupId>
+  <artifactId>xhr4j</artifactId>
+  <version>2.0-SNAPSHOT</version>
+  <packaging>bundle</packaging>
+  <name>XHR via Java</name>
+  <url>http://maven.apache.org</url>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.netbeans.html</groupId>
+                <artifactId>html4j-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <configuration>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <bundleSymbolicName>org.netbeans.html.xhr4j</bundleSymbolicName>
+  </properties>
+  <dependencies>
+    <!-- compile only deps -->
+    <dependency>
+      <groupId>org.netbeans.api</groupId>
+      <artifactId>org-openide-util-lookup</artifactId>
+      <type>jar</type>
+      <scope>provided</scope>
+    </dependency>
+
+    <!-- compile + runtime -->      
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>net.java.html</artifactId>
+      <version>${project.version}</version>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>net.java.html.json</artifactId>
+      <version>${project.version}</version>
+      <type>jar</type>
+    </dependency>
+    <!-- test only deps -->
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>net.java.html.boot</artifactId>
+      <version>${project.version}</version>
+      <scope>provided</scope>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>net.java.html.json.tck</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>ko4j</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+      <groupId>org.glassfish.grizzly</groupId>
+      <artifactId>grizzly-http-server-core</artifactId>
+      <version>${grizzly.version}</version>
+      <scope>test</scope>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+      <groupId>org.glassfish.grizzly</groupId>
+      <artifactId>grizzly-websockets-server</artifactId>
+      <version>${grizzly.version}</version>
+      <scope>test</scope>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+        <groupId>${project.groupId}</groupId>
+        <artifactId>net.java.html.boot.fx</artifactId>
+        <version>${project.version}</version>
+        <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.glassfish.grizzly</groupId>
+      <artifactId>grizzly-http-server</artifactId>
+      <version>${grizzly.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+        <groupId>org.glassfish.grizzly</groupId>
+        <artifactId>grizzly-http-servlet</artifactId>
+        <version>${grizzly.version}</version>
+        <scope>test</scope>
+    </dependency>    
+    <dependency>
+        <groupId>javax.servlet</groupId>
+        <artifactId>javax.servlet-api</artifactId>
+        <scope>test</scope>
+    </dependency>
+  </dependencies>
+    <description>Implementation module with support for XHR via Java.
+Use it to workaround CORS limitations.</description>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/xhr4j/src/main/java/org/netbeans/html/xhr4j/LoadJSON.java
----------------------------------------------------------------------
diff --git a/xhr4j/src/main/java/org/netbeans/html/xhr4j/LoadJSON.java b/xhr4j/src/main/java/org/netbeans/html/xhr4j/LoadJSON.java
new file mode 100644
index 0000000..78ac78a
--- /dev/null
+++ b/xhr4j/src/main/java/org/netbeans/html/xhr4j/LoadJSON.java
@@ -0,0 +1,274 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.xhr4j;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PushbackInputStream;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.logging.Logger;
+import net.java.html.js.JavaScriptBody;
+import org.netbeans.html.json.spi.JSONCall;
+
+/** This is an implementation package - just
+ * include its JAR on classpath and use official {@link Context} API
+ * to access the functionality.
+ *
+ * @author Jaroslav Tulach
+ */
+final class LoadJSON implements Runnable {
+    private static final Logger LOG = Logger.getLogger(LoadJSON.class.getName());
+    private static final Executor REQ = Executors.newCachedThreadPool(new ThreadFactory() {
+        @Override
+        public Thread newThread(Runnable runnable) {
+            Thread thread = Executors.defaultThreadFactory().newThread(runnable);
+            thread.setDaemon(true);
+            thread.setName("xhr4j daemon");
+            return thread;
+        }
+    });
+
+    private final JSONCall call;
+    private final URL base;
+
+
+    private LoadJSON(JSONCall call) {
+        this.call = call;
+        this.base = null;
+    }
+
+    public static void loadJSON(JSONCall call) {
+        assert !"WebSocket".equals(call.getMethod());
+        REQ.execute(new LoadJSON((call)));
+    }
+
+    @Override
+    public void run() {
+        final String url;
+        Throwable error = null;
+        Object json = null;
+
+        if (call.isJSONP()) {
+            url = call.composeURL("dummy");
+        } else {
+            url = call.composeURL(null);
+        }
+        try {
+            final URL u = new URL(base, url.replace(" ", "%20"));
+            URLConnection conn = u.openConnection();
+            if (call.isDoOutput()) {
+                conn.setDoOutput(true);
+            }
+            String h = call.getHeaders();
+            if (h != null) {
+                int pos = 0;
+                while (pos < h.length()) {
+                    int tagEnd = h.indexOf(':', pos);
+                    if (tagEnd == -1) {
+                        break;
+                    }
+                    int r = h.indexOf('\r', tagEnd);
+                    int n = h.indexOf('\n', tagEnd);
+                    if (r == -1) {
+                        r = h.length();
+                    }
+                    if (n == -1) {
+                        n = h.length();
+                    }
+                    String key = h.substring(pos, tagEnd).trim();
+                    String val = h.substring(tagEnd + 1, Math.min(r, n)).trim();
+                    conn.setRequestProperty(key, val);;
+                    pos = Math.max(r, n);
+                }
+            }
+            if (call.getMethod() != null && conn instanceof HttpURLConnection) {
+                ((HttpURLConnection) conn).setRequestMethod(call.getMethod());
+            }
+            if (call.isDoOutput()) {
+                final OutputStream os = conn.getOutputStream();
+                call.writeData(os);
+                os.flush();
+            }
+            final PushbackInputStream is = new PushbackInputStream(
+                conn.getInputStream(), 1
+            );
+            boolean[] arrayOrString = { false, false };
+            detectJSONType(call.isJSONP(), is, arrayOrString);
+            String response = readStream(is);
+            if (call.isJSONP()) {
+                response = '(' + response;
+            }
+            json = new Result(response, arrayOrString[0], arrayOrString[1]);
+        } catch (IOException ex) {
+            error = ex;
+        } finally {
+            if (error != null) {
+                call.notifyError(error);
+            } else {
+                call.notifySuccess(json);
+            }
+        }
+    }
+
+    private static final class Result implements Callable<Object> {
+        private final String response;
+        private final boolean array;
+        private final boolean plain;
+
+        Result(String response, boolean array, boolean plain) {
+            this.response = response;
+            this.array = array;
+            this.plain = plain;
+        }
+
+        @Override
+        public Object call() throws Exception {
+            if (plain) {
+                return response;
+            } else {
+                if (array) {
+                    Object r = parse(response);
+                    Object[] arr = r instanceof Object[] ? (Object[])r : new Object[] { r };
+                    for (int i = 0; i < arr.length; i++) {
+                        arr[i] = new JSObjToStr(response, arr[i]);
+                    }
+                    return arr;
+                } else {
+                    return new JSObjToStr(response, parse(response));
+                }
+            }
+        }
+    }
+    private static final class JSObjToStr {
+        final String str;
+        final Object obj;
+
+        public JSObjToStr(Object str, Object obj) {
+            this.str = str == null ? "" : str.toString();
+            this.obj = obj;
+        }
+
+        @Override
+        public String toString() {
+            return str;
+        }
+    }
+
+    static String readStream(InputStream is) throws IOException, UnsupportedEncodingException {
+        Reader r = new InputStreamReader(is, "UTF-8");
+        StringBuilder sb = new StringBuilder();
+        char[] arr = new char[4096];
+        for (;;) {
+            int len = r.read(arr);
+            if (len == -1) {
+                break;
+            }
+            sb.append(arr, 0, len);
+        }
+        return sb.toString();
+    }
+
+    private static void detectJSONType(boolean skipAnything, final PushbackInputStream is, boolean[] arrayOrString) throws IOException {
+        for (;;) {
+            int ch = is.read();
+            if (ch == -1) {
+                arrayOrString[1] = true;
+                break;
+            }
+            if (Character.isWhitespace(ch)) {
+                continue;
+            }
+
+            if (ch == '[') {
+                is.unread(ch);
+                arrayOrString[0] = true;
+                break;
+            }
+            if (ch == '{') {
+                is.unread(ch);
+                break;
+            }
+            if (!skipAnything) {
+                is.unread(ch);
+                arrayOrString[1] = true;
+                break;
+            }
+        }
+    }
+
+    @JavaScriptBody(args = {"object", "property"}, body =
+        "var ret;\n" + 
+        "if (property === null) ret = object;\n" + 
+        "else if (object === null) ret = null;\n" + 
+        "else ret = object[property];\n" + 
+        "return ret ? (typeof ko === 'undefined' ? ret : ko.utils.unwrapObservable(ret)) : null;"
+    )
+    private static Object getProperty(Object object, String property) {
+        return null;
+    }
+
+    @JavaScriptBody(args = {"s"}, body = "return eval('(' + s + ')');")
+    static Object parse(String s) {
+        throw new IllegalStateException("No parser context for " + s);
+    }
+
+    static void extractJSON(Object js, String[] props, Object[] values) {
+        if (js instanceof JSObjToStr) {
+            js = ((JSObjToStr)js).obj;
+        }
+        for (int i = 0; i < props.length; i++) {
+            values[i] = getProperty(js, props[i]);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/xhr4j/src/main/java/org/netbeans/html/xhr4j/XmlHttpResourceContext.java
----------------------------------------------------------------------
diff --git a/xhr4j/src/main/java/org/netbeans/html/xhr4j/XmlHttpResourceContext.java b/xhr4j/src/main/java/org/netbeans/html/xhr4j/XmlHttpResourceContext.java
new file mode 100644
index 0000000..4b3f02f
--- /dev/null
+++ b/xhr4j/src/main/java/org/netbeans/html/xhr4j/XmlHttpResourceContext.java
@@ -0,0 +1,88 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.xhr4j;
+
+import java.io.IOException;
+import java.io.InputStream;
+import net.java.html.json.OnReceive;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.json.spi.JSONCall;
+import org.netbeans.html.json.spi.Transfer;
+import org.openide.util.lookup.ServiceProvider;
+
+/** Implementation module with support for XHR via Java.
+ * Handles {@link OnReceive} requests by using Java to connect to given
+ * URL and then parsing it via JavaScript. Use this module if you have
+ * problems with CORS - as the Java connection isn't restricted by CORS
+ * rules.
+ * 
+ * Registers {@link Transfer} technology at position <code>50</code>.
+ * The {@link Contexts.Id} of the technology is <b>xhr4j</b>.
+ * 
+ * @author Jaroslav Tulach
+ * @since 1.3
+ */
+@Contexts.Id("xhr4j")
+@ServiceProvider(service = Contexts.Provider.class)
+public final class XmlHttpResourceContext
+implements Contexts.Provider, Transfer {
+    @Override
+    public void fillContext(Contexts.Builder context, Class<?> requestor) {
+        context.register(Transfer.class, this, 50);
+    }
+
+    @Override
+    public void extract(Object obj, String[] props, Object[] values) {
+        LoadJSON.extractJSON(obj, props, values);
+    }
+
+    @Override
+    public Object toJSON(InputStream is) throws IOException {
+        return LoadJSON.parse(LoadJSON.readStream(is));
+    }
+
+    @Override
+    public void loadJSON(JSONCall call) {
+        LoadJSON.loadJSON(call);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/xhr4j/src/test/java/org/netbeans/html/xhr4j/JsonDynamicHTTP.java
----------------------------------------------------------------------
diff --git a/xhr4j/src/test/java/org/netbeans/html/xhr4j/JsonDynamicHTTP.java b/xhr4j/src/test/java/org/netbeans/html/xhr4j/JsonDynamicHTTP.java
new file mode 100644
index 0000000..d114468
--- /dev/null
+++ b/xhr4j/src/test/java/org/netbeans/html/xhr4j/JsonDynamicHTTP.java
@@ -0,0 +1,262 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.xhr4j;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.glassfish.grizzly.PortRange;
+import org.glassfish.grizzly.http.server.HttpHandler;
+import org.glassfish.grizzly.http.server.HttpServer;
+import org.glassfish.grizzly.http.server.NetworkListener;
+import org.glassfish.grizzly.http.server.Request;
+import org.glassfish.grizzly.http.server.Response;
+import org.glassfish.grizzly.http.server.ServerConfiguration;
+import org.glassfish.grizzly.websockets.WebSocket;
+import org.glassfish.grizzly.websockets.WebSocketAddOn;
+import org.glassfish.grizzly.websockets.WebSocketApplication;
+import org.glassfish.grizzly.websockets.WebSocketEngine;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+final class JsonDynamicHTTP extends HttpHandler {
+    private static int resourcesCount;
+    private static List<Resource> resources;
+    private static ServerConfiguration conf;
+    private static HttpServer server;
+
+    private JsonDynamicHTTP() {
+    }
+
+    static URI initServer() throws Exception {
+        server = HttpServer.createSimpleServer(null, new PortRange(8080, 65535));
+        final WebSocketAddOn addon = new WebSocketAddOn();
+        for (NetworkListener listener : server.getListeners()) {
+            listener.registerAddOn(addon);
+        }
+        resources = new ArrayList<Resource>();
+
+        conf = server.getServerConfiguration();
+        final JsonDynamicHTTP dh = new JsonDynamicHTTP();
+
+        conf.addHttpHandler(dh, "/");
+
+        server.start();
+
+        return pageURL("http", server, "/test.html");
+    }
+
+    @Override
+    public void service(Request request, Response response) throws Exception {
+        if ("/test.html".equals(request.getRequestURI())) {
+            response.setContentType("text/html");
+            final InputStream is = JsonDynamicHTTP.class.getResourceAsStream("test.html");
+            copyStream(is, response.getOutputStream(), null);
+            return;
+        }
+        if ("/dynamic".equals(request.getRequestURI())) {
+            String mimeType = request.getParameter("mimeType");
+            List<String> params = new ArrayList<String>();
+            boolean webSocket = false;
+            for (int i = 0;; i++) {
+                String p = request.getParameter("param" + i);
+                if (p == null) {
+                    break;
+                }
+                if ("protocol:ws".equals(p)) {
+                    webSocket = true;
+                    continue;
+                }
+                params.add(p);
+            }
+            final String cnt = request.getParameter("content");
+            String mangle = cnt.replace("%20", " ").replace("%0A", "\n");
+            ByteArrayInputStream is = new ByteArrayInputStream(mangle.getBytes("UTF-8"));
+            URI url;
+            final Resource res = new Resource(is, mimeType, "/dynamic/res" + ++resourcesCount, params.toArray(new String[params.size()]));
+            if (webSocket) {
+                url = registerWebSocket(res);
+            } else {
+                url = registerResource(res);
+            }
+            response.getWriter().write(url.toString());
+            response.getWriter().write("\n");
+            return;
+        }
+
+        for (Resource r : resources) {
+            if (r.httpPath.equals(request.getRequestURI())) {
+                response.setContentType(r.httpType);
+                r.httpContent.reset();
+                String[] params = null;
+                if (r.parameters.length != 0) {
+                    params = new String[r.parameters.length];
+                    for (int i = 0; i < r.parameters.length; i++) {
+                        params[i] = request.getParameter(r.parameters[i]);
+                        if (params[i] == null) {
+                            if ("http.method".equals(r.parameters[i])) {
+                                params[i] = request.getMethod().toString();
+                            } else if ("http.requestBody".equals(r.parameters[i])) {
+                                Reader rdr = request.getReader();
+                                StringBuilder sb = new StringBuilder();
+                                for (;;) {
+                                    int ch = rdr.read();
+                                    if (ch == -1) {
+                                        break;
+                                    }
+                                    sb.append((char) ch);
+                                }
+                                params[i] = sb.toString();
+                            } else if (r.parameters[i].startsWith("http.header.")) {
+                                params[i] = request.getHeader(r.parameters[i].substring(12));
+                            }
+                        }
+                        if (params[i] == null) {
+                            params[i] = "null";
+                        }
+                    }
+                }
+
+                copyStream(r.httpContent, response.getOutputStream(), null, params);
+            }
+        }
+    }
+
+    private URI registerWebSocket(Resource r) {
+        WebSocketEngine.getEngine().register("", r.httpPath, new WS(r));
+        return pageURL("ws", server, r.httpPath);
+    }
+
+    private URI registerResource(Resource r) {
+        if (!resources.contains(r)) {
+            resources.add(r);
+            conf.addHttpHandler(this, r.httpPath);
+        }
+        return pageURL("http", server, r.httpPath);
+    }
+
+    private static URI pageURL(String proto, HttpServer server, final String page) {
+        NetworkListener listener = server.getListeners().iterator().next();
+        int port = listener.getPort();
+        try {
+            return new URI(proto + "://localhost:" + port + page);
+        } catch (URISyntaxException ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+
+    static final class Resource {
+
+        final InputStream httpContent;
+        final String httpType;
+        final String httpPath;
+        final String[] parameters;
+
+        Resource(InputStream httpContent, String httpType, String httpPath,
+            String[] parameters) {
+            httpContent.mark(Integer.MAX_VALUE);
+            this.httpContent = httpContent;
+            this.httpType = httpType;
+            this.httpPath = httpPath;
+            this.parameters = parameters;
+        }
+    }
+
+    static void copyStream(InputStream is, OutputStream os, String baseURL, String... params) throws IOException {
+        for (;;) {
+            int ch = is.read();
+            if (ch == -1) {
+                break;
+            }
+            if (ch == '$' && params.length > 0) {
+                int cnt = is.read() - '0';
+                if (baseURL != null && cnt == 'U' - '0') {
+                    os.write(baseURL.getBytes("UTF-8"));
+                } else {
+                    if (cnt >= 0 && cnt < params.length) {
+                        os.write(params[cnt].getBytes("UTF-8"));
+                    } else {
+                        os.write('$');
+                        os.write(cnt + '0');
+                    }
+                }
+            } else {
+                os.write(ch);
+            }
+        }
+    }
+
+    private static class WS extends WebSocketApplication {
+        private final Resource r;
+
+        private WS(Resource r) {
+            this.r = r;
+        }
+
+        @Override
+        public void onMessage(WebSocket socket, String text) {
+            try {
+                r.httpContent.reset();
+                ByteArrayOutputStream out = new ByteArrayOutputStream();
+                copyStream(r.httpContent, out, null, text);
+                String s = new String(out.toByteArray(), "UTF-8");
+                socket.send(s);
+            } catch (IOException ex) {
+                LOG.log(Level.WARNING, null, ex);
+            }
+        }
+        private static final Logger LOG = Logger.getLogger(WS.class.getName());
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/xhr4j/src/test/java/org/netbeans/html/xhr4j/JsonFX.java
----------------------------------------------------------------------
diff --git a/xhr4j/src/test/java/org/netbeans/html/xhr4j/JsonFX.java b/xhr4j/src/test/java/org/netbeans/html/xhr4j/JsonFX.java
new file mode 100644
index 0000000..5b301e2
--- /dev/null
+++ b/xhr4j/src/test/java/org/netbeans/html/xhr4j/JsonFX.java
@@ -0,0 +1,125 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.xhr4j;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import javafx.application.Platform;
+import org.netbeans.html.boot.impl.FnContext;
+import org.netbeans.html.boot.spi.Fn;
+import org.testng.ITest;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public final class JsonFX implements ITest, Runnable {
+    private final Fn.Presenter p;
+    private final Method m;
+    private Object result;
+    private Object inst;
+    private int count;
+
+    JsonFX(Fn.Presenter p, Method m) {
+        this.p = p;
+        this.m = m;
+    }
+
+    @Override
+    public String getTestName() {
+        return m.getName();
+    }
+
+    @Test
+    public synchronized void executeTest() throws Exception {
+        if (result == null) {
+            Platform.runLater(this);
+            wait();
+        }
+        if (result instanceof Exception) {
+            throw (Exception)result;
+        }
+        if (result instanceof Error) {
+            throw (Error)result;
+        }
+    }
+
+    @Override
+    public synchronized void run() {
+        boolean notify = true;
+        try {
+            FnContext.currentPresenter(p);
+            if (inst == null) {
+                inst = m.getDeclaringClass().newInstance();
+            }
+            result = m.invoke(inst);
+            if (result == null) {
+                result = this;
+            }
+        } catch (InvocationTargetException ex) {
+            Throwable r = ex.getTargetException();
+            if (r instanceof InterruptedException) {
+                if (count++ < 100) {
+                    notify = false;
+                    try {
+                        Thread.sleep(100);
+                    } catch (Exception ex1) {
+                        // ignore and continue
+                    }
+                    Platform.runLater(this);
+                    return;
+                }
+            }
+            result = r;
+        } catch (Exception ex) {
+            result = ex;
+        } finally {
+            if (notify) {
+                notifyAll();
+            }
+            FnContext.currentPresenter(null);
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/xhr4j/src/test/java/org/netbeans/html/xhr4j/JsonKnockoutTest.java
----------------------------------------------------------------------
diff --git a/xhr4j/src/test/java/org/netbeans/html/xhr4j/JsonKnockoutTest.java b/xhr4j/src/test/java/org/netbeans/html/xhr4j/JsonKnockoutTest.java
new file mode 100644
index 0000000..b84be22
--- /dev/null
+++ b/xhr4j/src/test/java/org/netbeans/html/xhr4j/JsonKnockoutTest.java
@@ -0,0 +1,217 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.xhr4j;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import net.java.html.BrwsrCtx;
+import net.java.html.boot.BrowserBuilder;
+import net.java.html.js.JavaScriptBody;
+import org.netbeans.html.boot.spi.Fn;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.json.spi.Technology;
+import org.netbeans.html.json.tck.KOTest;
+import org.netbeans.html.json.tck.KnockoutTCK;
+import org.netbeans.html.ko4j.KO4J;
+import org.openide.util.lookup.ServiceProvider;
+import org.testng.Assert;
+import static org.testng.Assert.*;
+import org.testng.annotations.Factory;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+@ServiceProvider(service = KnockoutTCK.class)
+public final class JsonKnockoutTest extends KnockoutTCK {
+    private static Class<?> browserClass;
+    private static Fn.Presenter browserContext;
+    
+    public JsonKnockoutTest() {
+    }
+    
+    @Factory public static Object[] compatibilityTests() throws Exception {
+        Class[] arr = testClasses();
+        for (int i = 0; i < arr.length; i++) {
+            assertEquals(arr[i].getClassLoader(),
+                JsonKnockoutTest.class.getClassLoader(),
+                "All classes loaded by the same classloader"
+            );
+        }
+        
+        URI uri = JsonDynamicHTTP.initServer();
+    
+        final BrowserBuilder bb = BrowserBuilder.newBrowser().loadClass(JsonKnockoutTest.class).
+            loadPage(uri.toString()).
+            invoke("initialized");
+        
+        Executors.newSingleThreadExecutor().submit(new Runnable() {
+            @Override
+            public void run() {
+                bb.showAndWait();
+            }
+        });
+        
+        ClassLoader l = getClassLoader();
+        List<Object> res = new ArrayList<Object>();
+        for (int i = 0; i < arr.length; i++) {
+            Class<?> c = Class.forName(arr[i].getName(), true, l);
+            Class<? extends Annotation> koTest = 
+                c.getClassLoader().loadClass(KOTest.class.getName()).
+                asSubclass(Annotation.class);
+            for (Method m : c.getMethods()) {
+                if (m.getAnnotation(koTest) != null) {
+                    res.add(new JsonFX(browserContext, m));
+                }
+            }
+        }
+        return res.toArray();
+    }
+
+    static synchronized ClassLoader getClassLoader() throws InterruptedException {
+        while (browserClass == null) {
+            JsonKnockoutTest.class.wait();
+        }
+        return browserClass.getClassLoader();
+    }
+    
+    public static synchronized void initialized(Class<?> browserCls) throws Exception {
+        browserClass = browserCls;
+        browserContext = Fn.activePresenter();
+        JsonKnockoutTest.class.notifyAll();
+    }
+    
+    public static void initialized() throws Exception {
+        Assert.assertSame(JsonKnockoutTest.class.getClassLoader(),
+            ClassLoader.getSystemClassLoader(),
+            "No special classloaders"
+        );
+        JsonKnockoutTest.initialized(JsonKnockoutTest.class);
+    }
+
+    @Override
+    public boolean canFailWebSocketTest() {
+        return true;
+    }
+    
+    @Override
+    public BrwsrCtx createContext() {
+        KO4J ko = new KO4J(browserContext);
+        XmlHttpResourceContext tc = new XmlHttpResourceContext();
+        Contexts.Builder cb = Contexts.newBuilder().
+            register(Technology.class, ko.knockout(), 10).
+            register(Executor.class, (Executor)browserContext, 10).
+            register(Fn.Presenter.class, (Fn.Presenter)browserContext, 10);
+        tc.fillContext(cb, browserClass);
+        return cb.build();
+    }
+
+    @Override
+    public Object createJSON(Map<String, Object> values) {
+        Object json = createJSON();
+        for (Map.Entry<String, Object> entry : values.entrySet()) {
+            setProperty(json, entry.getKey(), entry.getValue());
+        }
+        return json;
+    }
+
+    @JavaScriptBody(args = {}, body = "return new Object();")
+    private static native Object createJSON();
+
+    @JavaScriptBody(args = {"json", "key", "value"}, body = "json[key] = value;")
+    private static native void setProperty(Object json, String key, Object value);
+
+    @Override
+    @JavaScriptBody(args = { "s", "args" }, body = ""
+        + "var f = new Function(s); "
+        + "return f.apply(null, args);"
+    )
+    public native Object executeScript(String script, Object[] arguments);
+
+    @JavaScriptBody(args = {  }, body = 
+          "var h;"
+        + "if (!!window && !!window.location && !!window.location.href)\n"
+        + "  h = window.location.href;\n"
+        + "else "
+        + "  h = null;"
+        + "return h;\n"
+    )
+    private static native String findBaseURL();
+    
+    @Override
+    public URI prepareURL(String content, String mimeType, String[] parameters) {
+        try {
+            final URL baseURL = new URL(findBaseURL());
+            StringBuilder sb = new StringBuilder();
+            sb.append("/dynamic?mimeType=").append(mimeType);
+            for (int i = 0; i < parameters.length; i++) {
+                sb.append("&param" + i).append("=").append(parameters[i]);
+            }
+            String mangle = content.replace("\n", "%0a")
+                .replace("\"", "\\\"").replace(" ", "%20");
+            sb.append("&content=").append(mangle);
+
+            URL query = new URL(baseURL, sb.toString());
+            URLConnection c = query.openConnection();
+            BufferedReader br = new BufferedReader(new InputStreamReader(c.getInputStream()));
+            URI connectTo = new URI(br.readLine());
+            return connectTo;
+        } catch (IOException ex) {
+            throw new IllegalStateException(ex);
+        } catch (URISyntaxException ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/xhr4j/src/test/resources/org/netbeans/html/xhr4j/test.html
----------------------------------------------------------------------
diff --git a/xhr4j/src/test/resources/org/netbeans/html/xhr4j/test.html b/xhr4j/src/test/resources/org/netbeans/html/xhr4j/test.html
new file mode 100644
index 0000000..3951417
--- /dev/null
+++ b/xhr4j/src/test/resources/org/netbeans/html/xhr4j/test.html
@@ -0,0 +1,56 @@
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>XHR via Java Harness</title>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <meta name="viewport" content="width=device-width">
+    </head>
+    <body>
+        <h1>XHR via Java Harness</h1>
+    </body>
+    <script></script>
+</html>


[02/24] incubator-netbeans-html4j git commit: [INFRA-15006] Initial donation of HTML/Java NetBeans API. Equivalent to the content of html4j-donation-review.zip donated as part of ApacheNetBeansDonation1.zip with SHA256 being 7f2ca0f61953a190613c9a0fbcc1b

Posted by jt...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko4j/src/test/java/org/netbeans/html/ko4j/KOFx.java
----------------------------------------------------------------------
diff --git a/ko4j/src/test/java/org/netbeans/html/ko4j/KOFx.java b/ko4j/src/test/java/org/netbeans/html/ko4j/KOFx.java
new file mode 100644
index 0000000..0e9a8f3
--- /dev/null
+++ b/ko4j/src/test/java/org/netbeans/html/ko4j/KOFx.java
@@ -0,0 +1,132 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.ko4j;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javafx.application.Platform;
+import org.netbeans.html.boot.spi.Fn;
+import org.testng.ITest;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public final class KOFx implements ITest, Runnable {
+    private final Fn.Presenter p;
+    private final Method m;
+    private Object result;
+    private Object inst;
+    private int count;
+
+    KOFx(Fn.Presenter p, Method m) {
+        this.p = p;
+        this.m = m;
+    }
+
+    @Override
+    public String getTestName() {
+        return m.getName();
+    }
+
+    @Test
+    public synchronized void executeTest() throws Exception {
+        if (result == null) {
+            Platform.runLater(this);
+            wait();
+        }
+        if (result instanceof Exception) {
+            throw (Exception)result;
+        }
+        if (result instanceof Error) {
+            throw (Error)result;
+        }
+    }
+
+    @Override
+    public synchronized void run() {
+        boolean notify = true;
+        Closeable a = Fn.activate(p);
+        try {
+            if (inst == null) {
+                inst = m.getDeclaringClass().newInstance();
+            }
+            result = m.invoke(inst);
+            if (result == null) {
+                result = this;
+            }
+        } catch (InvocationTargetException ex) {
+            Throwable r = ex.getTargetException();
+            if (r instanceof InterruptedException) {
+                if (count++ < 10000) {
+                    notify = false;
+                    try {
+                        Thread.sleep(100);
+                    } catch (Exception ex1) {
+                        // ignore and continue
+                    }
+                    Platform.runLater(this);
+                    return;
+                }
+            }
+            result = r;
+        } catch (Exception ex) {
+            result = ex;
+        } finally {
+            if (notify) {
+                notifyAll();
+            }
+            try {
+                a.close();
+            } catch (IOException ex) {
+                throw new IllegalStateException(ex);
+            }
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko4j/src/test/java/org/netbeans/html/ko4j/KnockoutFXTest.java
----------------------------------------------------------------------
diff --git a/ko4j/src/test/java/org/netbeans/html/ko4j/KnockoutFXTest.java b/ko4j/src/test/java/org/netbeans/html/ko4j/KnockoutFXTest.java
new file mode 100644
index 0000000..306ac1e
--- /dev/null
+++ b/ko4j/src/test/java/org/netbeans/html/ko4j/KnockoutFXTest.java
@@ -0,0 +1,235 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.ko4j;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import net.java.html.BrwsrCtx;
+import net.java.html.boot.BrowserBuilder;
+import net.java.html.js.JavaScriptBody;
+import org.netbeans.html.boot.spi.Fn;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.json.spi.Technology;
+import org.netbeans.html.json.spi.Transfer;
+import org.netbeans.html.json.spi.WSTransfer;
+import org.netbeans.html.json.tck.KOTest;
+import org.netbeans.html.json.tck.KnockoutTCK;
+import org.openide.util.lookup.ServiceProvider;
+import org.testng.Assert;
+import static org.testng.Assert.*;
+import org.testng.annotations.Factory;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+@ServiceProvider(service = KnockoutTCK.class)
+public final class KnockoutFXTest extends KnockoutTCK {
+    private static Class<?> browserClass;
+    private static Fn.Presenter browserContext;
+    
+    public KnockoutFXTest() {
+    }
+    
+    @Factory public static Object[] compatibilityTests() throws Exception {
+        Class[] arr = testClasses();
+        for (int i = 0; i < arr.length; i++) {
+            assertEquals(
+                arr[i].getClassLoader(),
+                KnockoutFXTest.class.getClassLoader(),
+                "All classes loaded by the same classloader"
+            );
+        }
+        
+        URI uri = DynamicHTTP.initServer();
+    
+        final BrowserBuilder bb = BrowserBuilder.newBrowser().loadClass(KnockoutFXTest.class).
+            loadPage(uri.toString()).
+            invoke("initialized");
+        
+        Executors.newSingleThreadExecutor().submit(new Runnable() {
+            @Override
+            public void run() {
+                bb.showAndWait();
+            }
+        });
+        
+        ClassLoader l = getClassLoader();
+        List<Object> res = new ArrayList<Object>();
+        for (int i = 0; i < arr.length; i++) {
+            Class<?> c = Class.forName(arr[i].getName(), true, l);
+            seekKOTests(c, res);
+        }
+        Class<?> c = Class.forName(LessCallbacksCheck.class.getName(), true, l);
+        seekKOTests(c, res);
+        return res.toArray();
+    }
+
+    private static void seekKOTests(Class<?> c, List<Object> res) throws SecurityException, ClassNotFoundException {
+        Class<? extends Annotation> koTest =
+            c.getClassLoader().loadClass(KOTest.class.getName()).
+            asSubclass(Annotation.class);
+        for (Method m : c.getMethods()) {
+            if (m.getAnnotation(koTest) != null) {
+                res.add(new KOFx(browserContext, m));
+            }
+        }
+    }
+
+    static synchronized ClassLoader getClassLoader() throws InterruptedException {
+        while (browserClass == null) {
+            KnockoutFXTest.class.wait();
+        }
+        return browserClass.getClassLoader();
+    }
+    
+    public static synchronized void initialized(Class<?> browserCls) throws Exception {
+        browserClass = browserCls;
+        browserContext = Fn.activePresenter();
+        KnockoutFXTest.class.notifyAll();
+    }
+    
+    public static void initialized() throws Exception {
+        Assert.assertSame(
+            KnockoutFXTest.class.getClassLoader(),
+            ClassLoader.getSystemClassLoader(),
+            "No special classloaders"
+        );
+        KnockoutFXTest.initialized(KnockoutFXTest.class);
+        browserContext = Fn.activePresenter();
+    }
+    
+    @Override
+    public BrwsrCtx createContext() {
+        KO4J ko4j = new KO4J();
+        Contexts.Builder cb = Contexts.newBuilder().
+            register(Technology.class, ko4j.knockout(), 10).
+            register(Transfer.class, ko4j.transfer(), 10);
+        if (ko4j.websockets() != null) {
+            cb.register(WSTransfer.class, ko4j.websockets(), 10);
+        }
+        cb.register(Executor.class, (Executor)browserContext, 10);
+        cb.register(Fn.Presenter.class, browserContext, 10);
+        BrwsrCtx ctx = cb.build();
+        return ctx;
+    }
+
+    @Override
+    public Object createJSON(Map<String, Object> values) {
+        Object json = createJSON();
+        for (Map.Entry<String, Object> entry : values.entrySet()) {
+            setProperty(json, entry.getKey(), entry.getValue());
+        }
+        return json;
+    }
+    
+    @JavaScriptBody(args = {}, body = "return new Object();")
+    private static native Object createJSON();
+    @JavaScriptBody(args = { "json", "key", "value" }, body = "json[key] = value;")
+    private static native void setProperty(Object json, String key, Object value);
+
+    @Override
+    @JavaScriptBody(args = { "s", "args" }, body = ""
+        + "var f = new Function(s); "
+        + "return f.apply(null, args);"
+    )
+    public native Object executeScript(String script, Object[] arguments);
+
+    @JavaScriptBody(args = {  }, body = 
+          "var h;"
+        + "if (!!window && !!window.location && !!window.location.href)\n"
+        + "  h = window.location.href;\n"
+        + "else "
+        + "  h = null;"
+        + "return h;\n"
+    )
+    private static native String findBaseURL();
+    
+    @Override
+    public URI prepareURL(String content, String mimeType, String[] parameters) {
+        try {
+            final URL baseURL = new URL(findBaseURL());
+            StringBuilder sb = new StringBuilder();
+            sb.append("/dynamic?mimeType=").append(mimeType);
+            for (int i = 0; i < parameters.length; i++) {
+                sb.append("&param" + i).append("=").append(parameters[i]);
+            }
+            String mangle = content.replace("\n", "%0a")
+                .replace("\"", "\\\"").replace(" ", "%20");
+            sb.append("&content=").append(mangle);
+
+            URL query = new URL(baseURL, sb.toString());
+            URLConnection c = query.openConnection();
+            BufferedReader br = new BufferedReader(new InputStreamReader(c.getInputStream()));
+            URI connectTo = new URI(br.readLine());
+            return connectTo;
+        } catch (IOException ex) {
+            throw new IllegalStateException(ex);
+        } catch (URISyntaxException ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+
+    @Override
+    public boolean canFailWebSocketTest() {
+        try {
+            Class.forName("java.util.function.Function");
+            return false;
+        } catch (ClassNotFoundException ex) {
+            // running on JDK7, FX WebView WebSocket impl does not work
+            return true;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko4j/src/test/java/org/netbeans/html/ko4j/LessCallbacksCheck.java
----------------------------------------------------------------------
diff --git a/ko4j/src/test/java/org/netbeans/html/ko4j/LessCallbacksCheck.java b/ko4j/src/test/java/org/netbeans/html/ko4j/LessCallbacksCheck.java
new file mode 100644
index 0000000..81d643a
--- /dev/null
+++ b/ko4j/src/test/java/org/netbeans/html/ko4j/LessCallbacksCheck.java
@@ -0,0 +1,97 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.ko4j;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import net.java.html.json.ComputedProperty;
+import net.java.html.json.Model;
+import net.java.html.json.Property;
+import org.netbeans.html.json.tck.KOTest;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+@Model(className = "LessCalls", targetId = "", properties = {
+    @Property(name = "value", type = int.class)
+})
+public class LessCallbacksCheck {
+    private static StringWriter sw;
+    
+    @ComputedProperty static int plusOne(int value) {
+        if (sw == null) {
+            sw = new StringWriter();
+        }
+        new Exception("Who calls me?").printStackTrace(
+            new PrintWriter(sw)
+        );
+        return value + 1;
+    }
+    
+    @KOTest public void dontCallForInitialValueBackToJavaVM() {
+        sw = null;
+        LessCalls m = new LessCalls(10).applyBindings();
+        assert m.getPlusOne() == 11 : "Expecting 11: " + m.getPlusOne();
+        
+        assert sw != null : "StringWriter should be initialized: " + sw;
+        
+        if (sw.toString().contains("$JsCallbacks$")) {
+            assert false : "Don't call for initial value via JsCallbacks:\n" + sw;
+        }
+    }
+
+    @KOTest public void dontCallForChangeValueBackToJavaVM() {
+        LessCalls m = new LessCalls(10).applyBindings();
+        assert m.getPlusOne() == 11 : "Expecting 11: " + m.getPlusOne();
+        
+        sw = null;
+        m.setValue(5);
+        assert m.getPlusOne() == 6: "Expecting 6: " + m.getPlusOne();
+        assert sw != null : "StringWriter should be initialized: " + sw;
+        
+        if (sw.toString().contains("$JsCallbacks$")) {
+            assert false : "Don't call for initial value via JsCallbacks:\n" + sw;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko4j/src/test/java/org/netbeans/html/ko4j/OffThreadInitializationTest.java
----------------------------------------------------------------------
diff --git a/ko4j/src/test/java/org/netbeans/html/ko4j/OffThreadInitializationTest.java b/ko4j/src/test/java/org/netbeans/html/ko4j/OffThreadInitializationTest.java
new file mode 100644
index 0000000..71bbd65
--- /dev/null
+++ b/ko4j/src/test/java/org/netbeans/html/ko4j/OffThreadInitializationTest.java
@@ -0,0 +1,166 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.ko4j;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import net.java.html.BrwsrCtx;
+import net.java.html.json.Model;
+import net.java.html.json.Models;
+import net.java.html.json.Property;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.json.spi.FunctionBinding;
+import org.netbeans.html.json.spi.PropertyBinding;
+import org.netbeans.html.json.spi.Technology;
+import org.testng.Assert;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.fail;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+@Model(className = "Background", properties = {
+    @Property(name = "identityHashCode", type = int.class),
+})
+public class OffThreadInitializationTest {
+    private ScheduledExecutorService executor;
+
+    @BeforeMethod
+    public void initExecutor() {
+        executor = Executors.newSingleThreadScheduledExecutor();
+    }
+
+    @Test
+    public void backgroundInitializationOfAModel() throws Exception {
+        BrwsrCtx ctx = Contexts.newBuilder().register(Technology.class, new DummyTechnology(), 1).build();
+        DummyTechnology.assertEquals(1, "One technology, created explicitly");
+        final Background prototype = new Background();
+        DummyTechnology.assertEquals(0, "No more technology on rebind");
+        final Background b = Models.bind(prototype, ctx);
+        Models.applyBindings(b);
+        DummyTechnology.assertEquals(0, "Technology is shared!");
+
+
+        Background b2 = executor.submit(new Callable<Background>() {
+            @Override
+            public Background call() throws Exception {
+                return b.clone();
+            }
+        }).get();
+
+        assertSameTech(b, b2);
+        DummyTechnology.assertEquals(0, "Technology is still shared!");
+    }
+
+    private void assertSameTech(Background b, Background b2) {
+        assertEquals(b.getIdentityHashCode(), b2.getIdentityHashCode(), "The hashcodes of the tech has to be the same");
+    }
+
+    private static final class DummyTechnology implements Technology<Object> {
+        static int cnt;
+
+        static void assertAtMax(int i, String msg) {
+            if (cnt <= i) {
+                cnt = 0;
+            } else {
+                fail(msg + " was: " + cnt);
+            }
+        }
+
+        static void assertEquals(int i, String msg) {
+            Assert.assertEquals(cnt, i, msg);
+            cnt = 0;
+        }
+
+        DummyTechnology() {
+            cnt++;
+        }
+
+        @Override
+        public Object wrapModel(Object model) {
+            return model;
+        }
+
+        @Override
+        public <M> M toModel(Class<M> modelClass, Object data) {
+            return modelClass.cast(data);
+        }
+
+        @Override
+        public void bind(PropertyBinding b, Object model, Object data) {
+            if (b.getPropertyName().equals("identityHashCode")) {
+                ((Background)model).setIdentityHashCode(System.identityHashCode(this));
+            }
+        }
+
+        @Override
+        public void valueHasMutated(Object data, String propertyName) {
+        }
+
+        @Override
+        public void expose(FunctionBinding fb, Object model, Object d) {
+        }
+
+        @Override
+        public void applyBindings(Object data) {
+        }
+
+        @Override
+        public Object wrapArray(Object[] arr) {
+            return arr;
+        }
+
+        @Override
+        public void runSafe(Runnable r) {
+            r.run();
+        }
+    }
+
+    public static final class DummyProvider implements Contexts.Provider {
+        @Override
+        public void fillContext(Contexts.Builder context, Class<?> requestor) {
+            context.register(Technology.class, new DummyTechnology(), 0);
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko4j/src/test/java/org/netbeans/html/ko4j/ReferenceKnockoutTest.java
----------------------------------------------------------------------
diff --git a/ko4j/src/test/java/org/netbeans/html/ko4j/ReferenceKnockoutTest.java b/ko4j/src/test/java/org/netbeans/html/ko4j/ReferenceKnockoutTest.java
new file mode 100644
index 0000000..a730904
--- /dev/null
+++ b/ko4j/src/test/java/org/netbeans/html/ko4j/ReferenceKnockoutTest.java
@@ -0,0 +1,63 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.ko4j;
+
+import static org.testng.Assert.assertNull;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class ReferenceKnockoutTest {
+    @Test public void canLoadKnockout() {
+        Object ret = null;
+        try {
+            ret = Knockout.toModel(null);
+        } catch (IllegalStateException ex) {
+            // ISE is OK as we don't have any
+            // context for the 
+        }
+        assertNull(ret, "Either null");
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko4j/src/test/resources/org/netbeans/html/ko4j/test.html
----------------------------------------------------------------------
diff --git a/ko4j/src/test/resources/org/netbeans/html/ko4j/test.html b/ko4j/src/test/resources/org/netbeans/html/ko4j/test.html
new file mode 100644
index 0000000..d4399a0
--- /dev/null
+++ b/ko4j/src/test/resources/org/netbeans/html/ko4j/test.html
@@ -0,0 +1,56 @@
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>Knockout.fx Execution Harness</title>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <meta name="viewport" content="width=device-width">
+    </head>
+    <body>
+        <h1>Knockout.fx Execution Harness</h1>
+    </body>
+    <script></script>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..a19bfcb
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,475 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2016 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<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">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.netbeans.html</groupId>
+  <artifactId>pom</artifactId>
+  <version>2.0-SNAPSHOT</version>
+  <packaging>pom</packaging>
+  <name>HTML APIs via Java</name>
+  <parent> 
+    <groupId>net.java</groupId>
+    <artifactId>jvnet-parent</artifactId>
+     <version>3</version>
+  </parent>  
+  <properties>
+      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+      <netbeans.version>RELEASE80</netbeans.version>
+      <grizzly.version>2.3.8</grizzly.version>
+      <license>COPYING</license>
+      <publicPackages />
+      <bundleSymbolicName>${project.artifactId}</bundleSymbolicName>
+      <netbeans.compile.on.save>none</netbeans.compile.on.save>
+  </properties>
+  <modules>
+    <module>json</module>
+    <module>json-tck</module>
+    <module>ko4j</module>
+    <module>sound</module>
+    <module>context</module>
+    <module>boot</module>
+    <module>boot-fx</module>
+    <module>geo</module>
+    <module>ko-ws-tyrus</module>
+    <module>html4j-maven-plugin</module>
+    <module>ko-felix-test</module>
+    <module>ko-osgi-test</module>
+    <module>equinox-agentclass-hook</module>
+    <module>boot-script</module>
+    <module>boot-truffle</module>
+    <module>boot-agent-test</module>
+    <module>xhr4j</module>
+  </modules>
+  <licenses>
+      <license>
+          <name>GPL-2.0wCPexc+CDDL</name>
+          <url>http://www.netbeans.org/cddl-gplv2.html</url>
+          <distribution>repo</distribution>
+      </license>
+  </licenses>
+  <organization>
+      <name>NetBeans</name>
+      <url>http://netbeans.org</url>
+  </organization>
+  <scm>
+      <connection>scm:hg:https://hg.netbeans.org/html4j</connection>
+      <developerConnection>scm:hg:https://hg.netbeans.org/html4j</developerConnection>
+      <url>https://hg.netbeans.org/html4j</url>
+      <tag>default</tag>
+  </scm>
+  <repositories>
+      <repository>
+          <id>netbeans</id>
+          <name>NetBeans</name>
+          <url>http://bits.netbeans.org/maven2/</url>
+      </repository>
+  </repositories>
+  <pluginRepositories>
+      <pluginRepository>
+          <id>mc-release</id>
+          <name>Local Maven repository of releases</name>
+          <url>http://mc-repo.googlecode.com/svn/maven2/releases</url>
+          <snapshots>
+              <enabled>false</enabled>
+          </snapshots>
+          <releases>
+              <enabled>true</enabled>
+          </releases>
+      </pluginRepository>
+  </pluginRepositories>
+  <build>
+      <plugins>
+         <plugin>
+              <inherited>false</inherited>
+              <groupId>com.mycila.maven-license-plugin</groupId>
+              <artifactId>maven-license-plugin</artifactId>
+              <version>1.9.0</version>
+              <executions>
+                  <execution>
+                      <id>blah</id>
+                      <goals>
+                          <goal>check</goal>
+                      </goals>
+                  </execution>
+              </executions>
+              <configuration>
+                  <aggregate>true</aggregate>
+                  <basedir>${basedir}</basedir>
+                  <header>COPYING</header>
+                  <strictCheck>true</strictCheck>
+                  <excludes>
+                       <exclude>*</exclude>
+                       <exclude>.*/**</exclude>
+                       <exclude>*/nb-configuration.xml</exclude>
+                  </excludes>
+              </configuration>
+          </plugin>
+           <plugin>
+            <artifactId>maven-release-plugin</artifactId>
+            <version>2.4</version>
+            <configuration>
+              <mavenExecutorId>forked-path</mavenExecutorId>
+              <useReleaseProfile>false</useReleaseProfile>
+              <arguments>-Pjvnet-release -Pgpg</arguments>
+              <tag>release-${releaseVersion}</tag>
+            </configuration>
+          </plugin>
+            <plugin>
+              <groupId>org.apache.maven.plugins</groupId>
+              <artifactId>maven-javadoc-plugin</artifactId>
+              <version>2.9</version>
+              <configuration>
+                  <docfilessubdirs>true</docfilessubdirs>
+                  <subpackages>${publicPackages}</subpackages>
+                  <skip>false</skip>
+                  <excludePackageNames>
+org.netbeans.html.boot.impl:org.netbeans.html.boot.fx:org.netbeans.html.context.impl:org.netbeans.html.equinox.*:org.netbeans.html.geo.impl:org.netbeans.html.json.impl:org.netbeans.html.sound.impl:org.netbeans.html.ko.*:org.netbeans.html.ko4j:org.netbeans.html.mojo:org.netbeans.html.wstyrus:net.java.html.js.tests:net.java.html.json.tests:org.netbeans.html.xhr4j
+                  </excludePackageNames>
+                <groups>
+                  <group>
+                    <title>JSON for Java</title>
+                    <packages>net.java.html.json</packages>
+                  </group>
+                  <group>
+                    <title>Core Client APIs</title>
+                    <packages>net.java.html.boot*:net.java.html.js:net.java.html</packages>
+                  </group>
+                  <group>
+                    <title>Geolocation API</title>
+                    <packages>net.java.html.geo</packages>
+                  </group>
+                  <group>
+                    <title>Sound API</title>
+                    <packages>net.java.html.sound</packages>
+                  </group>
+                  <group>
+                    <title>Testing and Headless API</title>
+                    <packages>net.java.html.boot.script:net.java.html.boot.truffle</packages>
+                  </group>
+                  <group>
+                    <title>Service Provider APIs (not commonly interesting)</title>
+                    <packages>org.netbeans.html.*</packages>
+                  </group>
+                </groups>   
+                <links>
+                    <link>http://testng.org/javadocs/</link>
+                    <link>http://bits.netbeans.org/8.0/javadoc/org-openide-util-lookup/</link>
+                    <link>http://docs.oracle.com/javase/8/javafx/api/</link>
+                </links>              
+              </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.netbeans.tools</groupId>
+                <artifactId>sigtest-maven-plugin</artifactId>
+                <version>1.0</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>generate</goal>
+                            <goal>check</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <packages>${publicPackages}</packages>
+                    <releaseVersion>1.3</releaseVersion>
+                </configuration>
+            </plugin>
+      </plugins>
+      <pluginManagement>
+          <plugins>
+              <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>2.13</version>
+                <configuration>
+                    <systemPropertyVariables>
+                        <fxpresenter.headless>true</fxpresenter.headless>
+                    </systemPropertyVariables>
+                </configuration>
+              </plugin>
+              <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+                <version>2.18.1</version>
+                <configuration>
+                    <systemPropertyVariables>
+                        <fxpresenter.headless>true</fxpresenter.headless>
+                    </systemPropertyVariables>
+                </configuration>
+              </plugin>
+              <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-deploy-plugin</artifactId>
+                <version>2.8.1</version>
+              </plugin>
+              <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.1</version>
+                <configuration>
+                    <source>1.6</source>
+                    <target>1.6</target>
+                </configuration>
+              </plugin>
+              <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <version>2.9</version>
+                <configuration>
+                    <subpackages>${publicPackages}</subpackages>
+                    <skip>false</skip>
+                </configuration>
+              </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <version>2.4.0</version>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Export-Package>${publicPackages},META-INF.services.*;-noimport:=true;-split-package:=first</Export-Package>
+                        <Bundle-SymbolicName>${bundleSymbolicName}</Bundle-SymbolicName>
+                    </instructions>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.netbeans.html</groupId>
+                <artifactId>html4j-maven-plugin</artifactId>
+                <version>${project.version}</version>
+                <executions>
+                    <execution>
+                        <id>classes</id>
+                        <goals>
+                            <goal>process-js-annotations</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <id>test-classes</id>
+                        <phase>process-test-classes</phase>
+                        <goals>
+                            <goal>process-js-annotations</goal>
+                        </goals>
+                        <configuration>
+                            <classes>${project.build.directory}/test-classes</classes>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+          </plugins>
+      </pluginManagement>
+  </build>
+  <dependencyManagement>
+      <dependencies>
+        <dependency>
+          <groupId>org.testng</groupId>
+          <artifactId>testng</artifactId>
+          <version>6.7</version>
+          <scope>test</scope>
+          <exclusions>
+            <exclusion>
+              <artifactId>junit</artifactId>
+              <groupId>junit</groupId>
+            </exclusion>
+          </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>de.twentyeleven.skysail</groupId>
+            <artifactId>org.json-osgi</artifactId>
+            <version>20080701</version>
+        </dependency>
+        <dependency>
+            <groupId>org.ow2.asm</groupId>
+            <artifactId>asm</artifactId>
+            <version>5.0</version>
+        </dependency>
+        <dependency>
+          <groupId>org.netbeans.api</groupId>
+          <artifactId>org-netbeans-modules-classfile</artifactId>
+          <version>${netbeans.version}</version>
+          <type>jar</type>
+        </dependency>
+        <dependency>
+          <groupId>org.netbeans.api</groupId>
+          <artifactId>org-openide-util-lookup</artifactId>
+          <version>${netbeans.version}</version>
+          <scope>compile</scope>
+          <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>org.netbeans.api</groupId>
+            <artifactId>org-netbeans-api-annotations-common</artifactId>
+            <version>${netbeans.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.netbeans.api</groupId>
+            <artifactId>org-netbeans-modules-java-source</artifactId>
+            <version>${netbeans.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.netbeans.api</groupId>
+            <artifactId>org-netbeans-libs-javacapi</artifactId>
+            <version>${netbeans.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.netbeans.api</groupId>
+            <artifactId>org-netbeans-spi-java-hints</artifactId>
+            <version>${netbeans.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.netbeans.api</groupId>
+            <artifactId>org-netbeans-modules-parsing-api</artifactId>
+            <version>${netbeans.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.netbeans.api</groupId>
+            <artifactId>org-netbeans-spi-editor-hints</artifactId>
+            <version>${netbeans.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.netbeans.api</groupId>
+            <artifactId>org-openide-util</artifactId>
+            <version>${netbeans.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.netbeans.api</groupId>
+            <artifactId>org-netbeans-modules-java-lexer</artifactId>
+            <version>${netbeans.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.netbeans.api</groupId>
+            <artifactId>org-netbeans-modules-lexer</artifactId>
+            <version>${netbeans.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.netbeans.api</groupId>
+            <artifactId>org-netbeans-modules-java-hints-test</artifactId>
+            <version>${netbeans.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.netbeans.api</groupId>
+            <artifactId>org-netbeans-libs-junit4</artifactId>
+            <version>${netbeans.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.netbeans.modules</groupId>
+            <artifactId>org-netbeans-lib-nbjavac</artifactId>
+            <version>${netbeans.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.framework</artifactId>
+            <version>4.2.1</version>
+        </dependency>
+      <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <version>3.1.0</version>
+        </dependency>
+        <dependency> 
+            <groupId>org.netbeans.modules</groupId>
+            <artifactId>org-netbeans-modules-web-browser-api</artifactId>
+            <version>${netbeans.version}</version>
+            <exclusions>
+                <exclusion>
+                    <artifactId>org-netbeans-core</artifactId>
+                    <groupId>org.netbeans.modules</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>org-netbeans-core-multiview</artifactId>
+                    <groupId>org.netbeans.api</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>org-netbeans-libs-lucene</artifactId>
+                    <groupId>org.netbeans.api</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>org-netbeans-modules-diff</artifactId>
+                    <groupId>org.netbeans.api</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>org-netbeans-modules-editor-fold</artifactId>
+                    <groupId>org.netbeans.api</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>org-netbeans-modules-editor-guards</artifactId>
+                    <groupId>org.netbeans.api</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <artifactId>org-netbeans-modules-projectapi</artifactId>
+            <groupId>org.netbeans.api</groupId>
+            <type>jar</type>
+            <version>${netbeans.version}</version>
+        </dependency>
+      </dependencies>
+  </dependencyManagement>
+  <profiles>
+      <profile>
+          <id>jdk8</id>
+          <activation>
+              <file>
+                  <exists>${java.home}/lib/ext/jfxrt.jar</exists>
+              </file>
+          </activation>
+          <properties>
+            <jfxrt.jar>${java.home}/lib/ext/jfxrt.jar</jfxrt.jar>
+          </properties>
+      </profile>
+      <profile>
+          <id>jdk7</id>
+          <activation>
+              <file>
+                  <exists>${java.home}/lib/jfxrt.jar</exists>
+              </file>
+          </activation>
+          <properties>
+            <jfxrt.jar>${java.home}/lib/jfxrt.jar</jfxrt.jar>
+          </properties>
+      </profile>
+  </profiles>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/sound/pom.xml
----------------------------------------------------------------------
diff --git a/sound/pom.xml b/sound/pom.xml
new file mode 100644
index 0000000..62fce4b
--- /dev/null
+++ b/sound/pom.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2016 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<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">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.netbeans.html</groupId>
+        <artifactId>pom</artifactId>
+        <version>2.0-SNAPSHOT</version>
+    </parent>
+    <groupId>org.netbeans.html</groupId>
+    <artifactId>net.java.html.sound</artifactId>
+    <version>2.0-SNAPSHOT</version>
+    <packaging>bundle</packaging>
+    <name>Sound API via HTML</name>
+    <url>http://maven.apache.org</url>
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <publicPackages>net.java.html.sound</publicPackages>
+    </properties>
+    <build>
+        <plugins>
+          <plugin>
+              <groupId>org.apache.felix</groupId>
+              <artifactId>maven-bundle-plugin</artifactId>
+          </plugin>
+          <plugin>
+              <groupId>org.netbeans.html</groupId>
+              <artifactId>html4j-maven-plugin</artifactId>
+          </plugin>
+        </plugins>
+    </build>
+    <dependencies>
+        <dependency>
+            <groupId>org.testng</groupId>
+            <artifactId>testng</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.netbeans.html</groupId>
+            <artifactId>net.java.html.boot</artifactId>
+            <version>2.0-SNAPSHOT</version>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+          <groupId>org.netbeans.api</groupId>
+          <artifactId>org-openide-util-lookup</artifactId>
+          <scope>provided</scope>
+        </dependency>
+    </dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/sound/src/main/java/net/java/html/sound/AudioClip.java
----------------------------------------------------------------------
diff --git a/sound/src/main/java/net/java/html/sound/AudioClip.java b/sound/src/main/java/net/java/html/sound/AudioClip.java
new file mode 100644
index 0000000..d502077
--- /dev/null
+++ b/sound/src/main/java/net/java/html/sound/AudioClip.java
@@ -0,0 +1,200 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.sound;
+
+import java.util.ServiceLoader;
+import net.java.html.BrwsrCtx;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.sound.spi.AudioEnvironment;
+import org.netbeans.html.sound.impl.BrowserAudioEnv;
+
+/** Handle to an audio clip which can be {@link #play() played}, {@link #pause() paused}
+ * and etc. Obtain new instance via {@link #create(java.lang.String) create} factory 
+ * method and then use it when necessary.
+ *
+ * @author antonepple
+ */
+public abstract class AudioClip {
+    private AudioClip() {
+    }
+
+    /** Creates new instance of an audio clip based on the provided URL.
+     * If no suitable audio environment provider is found, the method 
+     * returns a dummy instance that does nothing and only returns
+     * false from its {@link #isSupported()} method.
+     * <p>
+     * The <code>src</code> can be absolute URL or it can be relative
+     * to current {@link BrwsrCtx browser context} - e.g. usually to the
+     * page that is just being displayed.
+     * 
+     * @param src the URL where to find the audio clip
+     * @return the audio clip handle
+     * @throws NullPointerException if src is <code>null</code>
+     */
+    public static AudioClip create(String src) {
+        src.getClass();
+        BrwsrCtx brwsrCtx = BrwsrCtx.findDefault(AudioClip.class);
+        AudioEnvironment brwsrAE = Contexts.find(brwsrCtx, AudioEnvironment.class);
+        if (brwsrAE != null) {
+            Impl handle = create(brwsrAE, src);
+            if (handle != null) {
+                return handle;
+            }
+        }
+        for (AudioEnvironment<?> ae : ServiceLoader.load(AudioEnvironment.class)) {
+            Impl handle = create(ae, src);
+            if (handle != null) {
+                return handle;
+            }
+        }
+        Impl handle = create(BrowserAudioEnv.DEFAULT, src);
+        return handle != null ? handle : DummyClip.INSTANCE;
+    }
+    
+    /** Plays the clip from begining to the end.
+     */
+    public abstract void play();
+
+    /** Pauses playback of the clip
+     */
+    public abstract void pause();
+
+    /**
+     * Specifies the volume of the audio. Must be a number between 0.0 and 1.0:
+     * <ul>
+     *   <li>1.0 - highest volume</li>
+     *   <li>0.5 - 50% volume</li>
+     *   <li>0.0 - silent</li>
+     * </ul>
+     * 
+     * @param volume for the playback
+     */
+    public abstract void setVolume(double volume);
+    
+    /** Check whether the audio clip is supported and can be played.
+     * @return true if it is likely that after calling {@link #play()} 
+     *   a sound will be produced
+     */
+    public abstract boolean isSupported();
+
+//    public abstract void playFrom(int seconds);
+
+    //
+    // Implementation
+    //
+    
+    private static <Audio> Impl<Audio> create(AudioEnvironment<Audio> env, String src) {
+        Audio a = env.create(src);
+        if (a != null) {
+            return new Impl<Audio>(env, src, a);
+        } else {
+            return null;
+        }
+    }
+    
+    private static final class Impl<Audio> extends AudioClip {
+        private final String src;
+        private final Audio clip;
+        private final AudioEnvironment<Audio> env;
+
+        public Impl(AudioEnvironment<Audio> env, String src, Audio clip) {
+            this.clip = clip;
+            this.env = env;
+            this.src = src;
+        }
+
+        @Override
+        public void play() {
+            env.play(clip);
+        }
+
+        @Override
+        public void pause() {
+            env.pause(clip);
+        }
+
+        @Override
+        public void setVolume(double volume) {
+            env.setVolume(clip, volume);
+        }
+
+        @Override
+        public boolean isSupported() {
+            return env.isSupported(clip);
+        }
+
+        @Override
+        public int hashCode() {
+            return 59 * src.hashCode();
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof Impl) {
+                return src.equals(((Impl)obj).src);
+            }
+            return false;
+        }
+    } // end of Impl
+    
+    private static final class DummyClip extends AudioClip {
+        static AudioClip INSTANCE = new DummyClip();
+        
+        @Override
+        public void play() {
+        }
+
+        @Override
+        public void pause() {
+        }
+
+        @Override
+        public void setVolume(double volume) {
+        }
+
+        @Override
+        public boolean isSupported() {
+            return false;
+        }
+    } // end of DummyClip
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/sound/src/main/java/net/java/html/sound/package.html
----------------------------------------------------------------------
diff --git a/sound/src/main/java/net/java/html/sound/package.html b/sound/src/main/java/net/java/html/sound/package.html
new file mode 100644
index 0000000..c41bf01
--- /dev/null
+++ b/sound/src/main/java/net/java/html/sound/package.html
@@ -0,0 +1,50 @@
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<body>
+    <p>
+        Control {@link net.java.html.sound.AudioClip sound} in your HTML for
+        Java applications.
+    </p>
+</body>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/sound/src/main/java/org/netbeans/html/sound/impl/BrowserAudioEnv.java
----------------------------------------------------------------------
diff --git a/sound/src/main/java/org/netbeans/html/sound/impl/BrowserAudioEnv.java b/sound/src/main/java/org/netbeans/html/sound/impl/BrowserAudioEnv.java
new file mode 100644
index 0000000..4d084bc
--- /dev/null
+++ b/sound/src/main/java/org/netbeans/html/sound/impl/BrowserAudioEnv.java
@@ -0,0 +1,85 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.sound.impl;
+
+import net.java.html.js.JavaScriptBody;
+import org.netbeans.html.sound.spi.AudioEnvironment;
+
+/** The default audio provider that delegates to HTML5 Audio tag
+ * it is used if no other {@link AudioEnvironment} is found.
+ *
+ * @author Jaroslav Tulach
+ */
+public final class BrowserAudioEnv implements AudioEnvironment<Object> {
+    public static final AudioEnvironment<?> DEFAULT = new BrowserAudioEnv();
+    
+    private BrowserAudioEnv() {
+    }
+    
+    @Override
+    @JavaScriptBody(args = { "src" }, body = ""
+        + "if (!Audio) return null;"
+        + "return new Audio(src);")
+    public Object create(String src) {
+        // null if not running in browser
+        return null;
+    }
+
+    @Override @JavaScriptBody(args = { "a" }, body = "a.play();")
+    public void play(Object a) {
+    }
+
+    @Override @JavaScriptBody(args = { "a" }, body = "a.pause();")
+    public void pause(Object a) {
+    }
+
+    @Override @JavaScriptBody(args = { "a", "volume" }, body = "a.setVolume(volume);")
+    public void setVolume(Object a, double volume) {
+    }
+
+    @Override
+    @JavaScriptBody(args = "a", body = "return true;")
+    public boolean isSupported(Object a) {
+        return false;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/sound/src/main/java/org/netbeans/html/sound/spi/AudioEnvironment.java
----------------------------------------------------------------------
diff --git a/sound/src/main/java/org/netbeans/html/sound/spi/AudioEnvironment.java b/sound/src/main/java/org/netbeans/html/sound/spi/AudioEnvironment.java
new file mode 100644
index 0000000..e8e6b82
--- /dev/null
+++ b/sound/src/main/java/org/netbeans/html/sound/spi/AudioEnvironment.java
@@ -0,0 +1,94 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.sound.spi;
+
+import net.java.html.BrwsrCtx;
+import org.netbeans.html.context.spi.Contexts;
+
+/** Basic interface for sound playback providers. Register your implementation
+ * in a way {@link java.util.ServiceLoader} can find it - e.g. use
+ * {@link org.openide.util.lookup.ServiceProvider} annotation. Possibly
+ * one can register the provider into {@link Contexts#newBuilder()}, in
+ * case the implementation is somehow associated 
+ * with the actual {@link BrwsrCtx} (works since version 0.8.3).
+ *
+ * @author antonepple
+ * @param <Audio> custom type representing the internal audio state
+ */
+public interface AudioEnvironment<Audio> {
+    /** Checks if the provided URL can be a supported audio stream 
+     * and if so, it create an object to represent it. The created object
+     * will be used in future callbacks to other methods of this interface
+     * (like {@link #play(java.lang.Object)}).
+     * @param src the URL pointing to the media stream
+     * @return an internal representation object or <code>null</code> if this
+     *   environment does not know how to handle such stream
+     */
+    public Audio create(String src);
+
+    /** Starts playback of the audio.
+     * 
+     * @param a the internal representation of the audio as created by {@link #create(java.lang.String)} method.
+     */
+    public void play(Audio a);
+
+    /** Pauses playback of the audio.
+     * 
+     * @param a the internal representation of the audio as created by {@link #create(java.lang.String)} method.
+     */
+    public void pause(Audio a);
+
+    /** Changes volume for the playback of the audio.
+     * 
+     * @param a the internal representation of the audio as created by {@link #create(java.lang.String)} method.
+     * @param volume value between 0.0 and 1.0
+     */
+    public void setVolume(Audio a, double volume);
+
+    /** Checks whether given audio is supported
+     * 
+     * @param a the internal representation of the audio as created by {@link #create(java.lang.String)} method.
+     * @return <code>true</code> or <code>false</code>
+     */
+    public boolean isSupported(Audio a);
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/sound/src/main/java/org/netbeans/html/sound/spi/package.html
----------------------------------------------------------------------
diff --git a/sound/src/main/java/org/netbeans/html/sound/spi/package.html b/sound/src/main/java/org/netbeans/html/sound/spi/package.html
new file mode 100644
index 0000000..b0e71cd
--- /dev/null
+++ b/sound/src/main/java/org/netbeans/html/sound/spi/package.html
@@ -0,0 +1,49 @@
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<body>
+    <p>
+        Give your {@link net.java.html.sound.AudioClip} real behavior!
+    </p>
+</body>


[08/24] incubator-netbeans-html4j git commit: [INFRA-15006] Initial donation of HTML/Java NetBeans API. Equivalent to the content of html4j-donation-review.zip donated as part of ApacheNetBeansDonation1.zip with SHA256 being 7f2ca0f61953a190613c9a0fbcc1b

Posted by jt...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/main/java/org/netbeans/html/json/spi/Technology.java
----------------------------------------------------------------------
diff --git a/json/src/main/java/org/netbeans/html/json/spi/Technology.java b/json/src/main/java/org/netbeans/html/json/spi/Technology.java
new file mode 100644
index 0000000..b53f05e
--- /dev/null
+++ b/json/src/main/java/org/netbeans/html/json/spi/Technology.java
@@ -0,0 +1,216 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.json.spi;
+
+import net.java.html.BrwsrCtx;
+import net.java.html.json.Model;
+import net.java.html.json.Models;
+import org.netbeans.html.context.spi.Contexts.Id;
+
+/** An implementation of a binding between model classes (see {@link Model})
+ * and particular technology like <a href="http://knockoutjs.com">knockout.js</a>
+ * in a browser window, etc.
+ * Since introduction of {@link Id technology identifiers} one can choose between
+ * different background implementations to handle the conversion and
+ * communication requests. The currently known provider is
+ * <code>org.netbeans.html:ko4j</code> module which registers 
+ * a <a href="http://knockoutjs.com" target="_blank">knockout.js</a>
+ * implementation called <b>ko4j</b>.
+ *
+ * @author Jaroslav Tulach
+ */
+public interface Technology<Data> {
+    /** Creates an object to wrap the provided model object. The model
+     * has previously been generated by annotation processor associated 
+     * with {@link Model} annotation.
+     * 
+     * @param model the model generated from {@link Model}
+     * @return internal object representing the model
+     */
+    public Data wrapModel(Object model);
+    
+    /** Converts an element potentially representing a model into the model.
+     * @param <M> the type of the <code>modelClass</code>
+     * @param modelClass expected class to convert the data to
+     * @param data the current data provided from the browser
+     * @return the instance of modelClass somehow extracted from the data, may return <code>null</code>
+     */
+    public <M> M toModel(Class<M> modelClass, Object data);
+    
+    /** Binds a property between the model and the data as used by the technology.
+     * 
+     * @param b the description of the requested binding
+     * @param model the original instance of the model
+     * @param data the data to bind with the model
+     */
+    public void bind(PropertyBinding b, Object model, Data data);
+
+    /** Model for given data has changed its value. The technology is
+     * supposed to update its state (for example DOM nodes associated
+     * with the model). The update usually happens asynchronously.
+     * 
+     * @param data technology's own representation of the model
+     * @param propertyName name of the model property that changed
+     */
+    public void valueHasMutated(Data data, String propertyName);
+
+    public void expose(FunctionBinding fb, Object model, Data d);
+    
+    /** Applies given data to current context (usually an HTML page).
+     * @param data the data to apply
+     */
+    public void applyBindings(Data data);
+    
+    /**
+     * Some technologies may require wrapping a Java array into a special
+     * object. In such case they may return it from this method.
+     *
+     * @param arr original array
+     * @return wrapped array
+     */
+    public Object wrapArray(Object[] arr);
+    
+    /** 
+     * Run given runnable in a safe mode. If the runnable can be executed
+     * immediately, do it. If we need to switch to some other thread, do it
+     * and invoke r asynchronously immediately returning from the call to
+     * runSafe method.
+     * 
+     * @param r the runnable to execute
+     * @deprecated Use {@link BrwsrCtx#execute(java.lang.Runnable)}
+     */
+    @Deprecated
+    public void runSafe(Runnable r);
+
+    /** For certain rendering technologies it may be more efficient to register
+     * property and function bindings for one instance of the model at once, 
+     * rather then doing it incrementally via 
+     * {@link Technology#expose(org.netbeans.html.json.spi.FunctionBinding, java.lang.Object, java.lang.Object) }
+     * and 
+     * {@link Technology#bind(org.netbeans.html.json.spi.PropertyBinding, java.lang.Object, java.lang.Object) }.
+     * In such case implement the {@link #wrapModel(java.lang.Object, org.netbeans.html.json.spi.PropertyBinding[], org.netbeans.html.json.spi.FunctionBinding[]) }
+     * method of this interface and it will be called instead of the 
+     * previous two ones.
+     * 
+     * @since 0.6
+     */
+    public static interface BatchInit<D> extends Technology<D> {
+        /** Wrap the given model into rendering technology appropriate object
+         * <code>D</code> and expose given properties and functions on it.
+         * 
+         * @param model the {@link Models#isModel(java.lang.Class) model} in Java
+         * @param propArr array of property bindings to expose
+         * @param funcArr array of functions to expose
+         * @return appropriate wrapper around the model
+         */
+        public D wrapModel(Object model, PropertyBinding[] propArr, FunctionBinding[] funcArr);
+    }
+
+    /** Some technologies are more effective when number of calls between
+     * Java and JavaScript is limited - to do that when a value of property
+     * is changed they should implement this additional interface.
+     * 
+     * @param <D> internal type of the technology
+     * @since 0.7.6
+     */
+    public static interface ValueMutated<D> extends Technology<D> {
+        /** Model for given data has changed its value. The technology is
+         * supposed to update its state (for example DOM nodes associated
+         * with the model). The update usually happens asynchronously.
+         * <p>
+         * If both <code>oldValue</code> and <code>newValue</code> are 
+         * <code>null</code> then the real value of the technology is
+         * not known.
+         * <p>
+         * If this method is present, then it is called instead of 
+         * old, plain {@link #valueHasMutated(java.lang.Object, java.lang.String)}
+         * which is never called by the infrastructure then.
+         * 
+         * @param data technology's own representation of the model
+         * @param propertyName name of the model property that changed
+         * @param oldValue provides previous value of the property
+         * @param newValue provides new value of the property
+         */
+        public void valueHasMutated(D data, String propertyName, Object oldValue, Object newValue);
+    }
+    
+    /** Apply technology bindings at selected subtree of the HTML page.
+     * Can be accessed via {@link Proto#applyBindings(java.lang.String)} or
+     * via method <code>applyBindings(String)</code> generated when one
+     * is using the {@link Model} annotation.
+     * 
+     * @param <D> the internal data for the technology
+     * @since 1.1
+     */
+    public static interface ApplyId<D> extends Technology<D> {
+        /** Applies given data to current context (usually an element on an 
+         * HTML page).
+         * 
+         * @param id the id of an element to apply the data to
+         * @param data the data to apply
+         */
+        public void applyBindings(String id, D data);
+    }
+
+    /** Extension of {@link BatchInit} with enhanced support for
+     * copying values. Technologies that support this interface provide a
+     * guarantee that result {@link Models#toRaw(java.lang.Object)}
+     * wrapped by {@link Models#fromRaw(net.java.html.BrwsrCtx, java.lang.Class, java.lang.Object)}
+     * will share essential properties (and not just values) of the original object.
+     *
+     * @since 1.3
+     */
+    public static interface BatchCopy<D> extends Technology<D> {
+        /** Wrap the given model into rendering technology appropriate object
+         * <code>D</code> and expose given properties and functions on it.
+         *
+         * @param model the {@link Models#isModel(java.lang.Class) model} in Java
+         * @param copyFrom the object to copy data from
+         *      (expectably of type D, but that isn't guaranteed) or <code>null</code>
+         * @param propArr array of property bindings to expose
+         * @param funcArr array of functions to expose
+         * @return appropriate wrapper around the model
+         */
+        public D wrapModel(Object model, Object copyFrom, PropertyBinding[] propArr, FunctionBinding[] funcArr);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/main/java/org/netbeans/html/json/spi/Transfer.java
----------------------------------------------------------------------
diff --git a/json/src/main/java/org/netbeans/html/json/spi/Transfer.java b/json/src/main/java/org/netbeans/html/json/spi/Transfer.java
new file mode 100644
index 0000000..b70371f
--- /dev/null
+++ b/json/src/main/java/org/netbeans/html/json/spi/Transfer.java
@@ -0,0 +1,96 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.json.spi;
+
+import java.io.IOException;
+import java.io.InputStream;
+import org.netbeans.html.context.spi.Contexts.Builder;
+import org.netbeans.html.context.spi.Contexts.Id;
+
+/** A {@link Builder service provider interface} responsible for 
+ * conversion of JSON objects to Java ones and vice-versa.
+ * Since introduction of {@link Id technology identifiers} one can choose between
+ * different background implementations to handle the conversion and
+ * communication requests. The known providers include
+ * <code>org.netbeans.html:ko4j</code> module which registers 
+ * a native browser implementation called <b>xhr</b>, and a
+ * <code>org.netbeans.html:ko-ws-tyrus</code> module which registers 
+ * Java based implementation named <b>tyrus</b>.
+ *
+ * @author Jaroslav Tulach
+ */
+public interface Transfer {
+    /**
+     * Called to inspect properties of an object (usually a JSON or JavaScript
+     * wrapper).
+     *
+     * @param obj the object to inspect
+     * @param props the names of properties to check on the object
+     * <code>obj</code>
+     * @param values array of the same length as <code>props</code> should be
+     * filled by values of properties on the <code>obj</code>. If a property is
+     * not defined, a <code>null</code> value should be stored in the array
+     */
+    public void extract(Object obj, String[] props, Object[] values);
+    
+    /** Reads content of a stream and creates its JSON representation.
+     * The returned object is implementation dependant. It however needs
+     * to be acceptable as first argument of {@link #extract(java.lang.Object, java.lang.String[], java.lang.Object[]) extract}
+     * method. If the stream contains representation or a JSON array,
+     * an Object[] should be returned - each of its members should, by itself
+     * be acceptable argument to 
+     * the {@link #extract(java.lang.Object, java.lang.String[], java.lang.Object[]) extract} method.
+     * 
+     * @param is input stream to read data from
+     * @return an object representing the JSON data
+     * @throws IOException if something goes wrong
+     */
+    public Object toJSON(InputStream is) throws IOException;
+    
+    /** Starts the JSON or JSONP query. 
+     * 
+     * @param call description of the call to make
+     */
+    public void loadJSON(JSONCall call);
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/main/java/org/netbeans/html/json/spi/WSTransfer.java
----------------------------------------------------------------------
diff --git a/json/src/main/java/org/netbeans/html/json/spi/WSTransfer.java b/json/src/main/java/org/netbeans/html/json/spi/WSTransfer.java
new file mode 100644
index 0000000..16a8882
--- /dev/null
+++ b/json/src/main/java/org/netbeans/html/json/spi/WSTransfer.java
@@ -0,0 +1,92 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.json.spi;
+
+import net.java.html.BrwsrCtx;
+import org.netbeans.html.context.spi.Contexts.Provider;
+import org.netbeans.html.context.spi.Contexts.Id;
+
+/** Interface for providers of WebSocket protocol. Register into a 
+ * {@link BrwsrCtx context} in your own {@link Provider}.
+ * Since introduction of {@link Id technology identifiers} one can choose between
+ * different background implementations to handle the conversion and
+ * communication requests. The known providers include
+ * <code>org.netbeans.html:ko4j</code> module which registers 
+ * a native browser implementation called <b>websocket</b>, and a
+ * <code>org.netbeans.html:ko-ws-tyrus</code> module which registers 
+ * Java based implementation named <b>tyrus</b>.
+ *
+ * @author Jaroslav Tulach
+ * @param <WebSocket> internal implementation type representing the socket
+ * @since 0.5
+ */
+public interface WSTransfer<WebSocket> {
+    /** Initializes a web socket. The <code>callback</code> object should 
+     * have mostly empty values: {@link JSONCall#isDoOutput()} should be 
+     * <code>false</code> and thus there should be no {@link JSONCall#getMessage()}.
+     * The method of connection {@link JSONCall#getMethod()} is "WebSocket".
+     * Once the connection is open call {@link JSONCall#notifySuccess(java.lang.Object) notifySuccess(null)}.
+     * When the server sends some data then, pass them to 
+     * {@link JSONCall#notifySuccess(java.lang.Object) notifySuccess} method
+     * as well. If there is an error call {@link JSONCall#notifyError(java.lang.Throwable)}.
+     * 
+     * @param url the URL to connect to
+     * @param callback a way to provide results back to the client
+     * @return your object representing the established web socket
+     */
+    public WebSocket open(String url, JSONCall callback);
+
+    /** Sends data to the server. The most important value
+     * of the <code>data</code> parameter is {@link JSONCall#getMessage()},
+     * rest can be ignored.
+     * 
+     * @param socket internal representation of the socket
+     * @param data the message to be sent
+     */
+    public void send(WebSocket socket, JSONCall data);
+
+    /** A request to close the socket.
+     * @param socket internal representation of the socket
+     */
+    public void close(WebSocket socket);
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/main/java/org/netbeans/html/json/spi/package.html
----------------------------------------------------------------------
diff --git a/json/src/main/java/org/netbeans/html/json/spi/package.html b/json/src/main/java/org/netbeans/html/json/spi/package.html
new file mode 100644
index 0000000..1912dd1
--- /dev/null
+++ b/json/src/main/java/org/netbeans/html/json/spi/package.html
@@ -0,0 +1,51 @@
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<html>
+    <body>
+        <div>Service Provider Interfaces for those who wish to integrate own
+            <a href="Technology.html">technology</a> with the HTML for Java API.
+        </div>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/main/resources/org/netbeans/html/json/impl/Bundle.properties
----------------------------------------------------------------------
diff --git a/json/src/main/resources/org/netbeans/html/json/impl/Bundle.properties b/json/src/main/resources/org/netbeans/html/json/impl/Bundle.properties
new file mode 100644
index 0000000..489c7be
--- /dev/null
+++ b/json/src/main/resources/org/netbeans/html/json/impl/Bundle.properties
@@ -0,0 +1,97 @@
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+#
+# Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+# Other names may be trademarks of their respective owners.
+#
+# The contents of this file are subject to the terms of either the GNU
+# General Public License Version 2 only ("GPL") or the Common
+# Development and Distribution License("CDDL") (collectively, the
+# "License"). You may not use this file except in compliance with the
+# License. You can obtain a copy of the License at
+# http://www.netbeans.org/cddl-gplv2.html
+# or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+# specific language governing permissions and limitations under the
+# License.  When distributing the software, include this License Header
+# Notice in each file and include the License file at
+# nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle in the GPL Version 2 section of the License file that
+# accompanied this code. If applicable, add the following below the
+# License Header, with the fields enclosed by brackets [] replaced by
+# your own identifying information:
+# "Portions Copyrighted [year] [name of copyright owner]"
+#
+# Contributor(s):
+#
+# The Original Software is NetBeans. The Initial Developer of the Original
+# Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+#
+# If you wish your version of this file to be governed by only the CDDL
+# or only the GPL Version 2, indicate your decision by adding
+# "[Contributor] elects to include this software in this distribution
+# under the [CDDL or GPL Version 2] license." If you do not indicate a
+# single choice of license, a recipient has the option to distribute
+# your version of this file under either the CDDL, the GPL Version 2 or
+# to extend the choice of license to its licensees as provided above.
+# However, if you add GPL Version 2 code and therefore, elected the GPL
+# Version 2 license, then the option applies only if the new code is
+# made subject to such option by the copyright holder.
+#
+
+MSG_Completion_GET=The GET method means retrieve whatever information \
+ (in the form of an entity) is identified by the Request-URI. \
+ If the Request-URI refers to a data-producing process, \
+ it is the produced data which shall be returned as the entity in \
+ the response and not the source text of the process, \
+ unless that text happens to be the output of the process.
+
+MSG_Completion_HEAD=The HEAD method is identical to GET except that the server \
+ MUST NOT return a message-body in the response. The metainformation \
+ contained in the HTTP headers in response to a HEAD request SHOULD be \
+ identical to the information sent in response to a GET request. \
+ This method can be used for obtaining metainformation about the entity implied \
+ by the request without transferring the entity-body itself. \
+ This method is often used for testing hypertext links for validity, \
+ accessibility, and recent modification.
+
+MSG_Completion_POST=The POST method is used to request that the origin server \
+ accept the entity enclosed in the request as a new subordinate of the resource \
+ identified by the Request-URI in the Request-Line. POST is designed to allow \
+ a uniform method to cover annotation of existing resources,\ 
+ posting a message to a bulletin board, newsgroup, mailing list, or similar \
+ group of articles, providing a block of data, such as the result of submitting a \
+ form, to a data-handling process or extending a database through an append operation. \
+ The actual function performed by the POST method is determined by the server \
+ and is usually dependent on the Request-URI. The posted entity is subordinate \
+ to that URI in the same way that a file is subordinate to a directory containing it, \
+ a news article is subordinate to a newsgroup to which it is posted, \
+ or a record is subordinate to a database.
+
+MSG_Completion_PUT=The PUT method requests that the enclosed entity be stored \
+ under the supplied Request-URI. If the Request-URI refers to an already \
+ existing resource, the enclosed entity SHOULD be considered as a modified \
+ version of the one residing on the origin server. If the Request-URI does \
+ not point to an existing resource, and that URI is capable of being defined \
+ as a new resource by the requesting user agent, the origin server can \
+ create the resource with that URI. If a new resource is created, the origin \
+ server MUST inform the user agent via the 201 (Created) response. \
+ If an existing resource is modified, either the 200 (OK) or 204 (No Content) \
+ response codes SHOULD be sent to indicate successful completion of the request. \
+ If the resource could not be created or modified with the Request-URI, an \
+ appropriate error response SHOULD be given that reflects the nature of the problem. \
+ The recipient of the entity MUST NOT ignore any Content-* (e.g. Content-Range) \
+ headers that it does not understand or implement and MUST return \
+ a 501 (Not Implemented) response in such cases.
+
+MSG_Completion_DELETE=The DELETE method requests that the origin server delete \
+ the resource identified by the Request-URI. This method MAY be overridden \
+ by human intervention (or other means) on the origin server. The client \
+ cannot be guaranteed that the operation has been carried out, even if \
+ the status code returned from the origin server indicates that the action \
+ has been completed successfully. However, the server SHOULD NOT indicate \
+ success unless, at the time the response is given, it intends to delete \
+ the resource or move it to an inaccessible location.
+

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/main/resources/org/netbeans/html/json/spi/package.html
----------------------------------------------------------------------
diff --git a/json/src/main/resources/org/netbeans/html/json/spi/package.html b/json/src/main/resources/org/netbeans/html/json/spi/package.html
new file mode 100644
index 0000000..80517a4
--- /dev/null
+++ b/json/src/main/resources/org/netbeans/html/json/spi/package.html
@@ -0,0 +1,59 @@
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<!DOCTYPE html>
+<html>
+    <head>
+        <title></title>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    </head>
+    <body>
+        <div>Implement 
+            <a href="Technology.html">Technology</a> and
+            <a href="Transfer.html">Transfer</a> and use 
+            <a href="ContextBuilder.html">ContextBuilder</a> to create an instance
+            of <code>Context</code> representing your technology.
+        </div>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/net/java/html/json/AdressTest.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/net/java/html/json/AdressTest.java b/json/src/test/java/net/java/html/json/AdressTest.java
new file mode 100644
index 0000000..8388fab
--- /dev/null
+++ b/json/src/test/java/net/java/html/json/AdressTest.java
@@ -0,0 +1,59 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json;
+
+import net.java.html.json.Model;
+import net.java.html.json.Property;
+import static org.testng.Assert.assertNotNull;
+import org.testng.annotations.Test;
+
+@Model(className = "Address", properties = {
+    @Property(name = "street", type = net.java.html.json.sub.Street.class)
+})
+public class AdressTest {
+    @Test
+    public void addressHoldsAPerson() {
+        Address address = new Address();
+        assertNotNull(address.getStreet(), "Street is initialized");
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/net/java/html/json/BoardTest.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/net/java/html/json/BoardTest.java b/json/src/test/java/net/java/html/json/BoardTest.java
new file mode 100644
index 0000000..4b8d645
--- /dev/null
+++ b/json/src/test/java/net/java/html/json/BoardTest.java
@@ -0,0 +1,83 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json;
+
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+@Model(className = "Board", properties = {
+    @Property(name = "rows", type = Row.class, array = true)
+})
+public class BoardTest {
+    
+    @Model(className = "Row", properties = {
+        @Property(name = "column", type = Column.class, array = true)
+    })
+    static class RowModel {
+    }
+    
+    @Model(className = "Column", properties = {
+        @Property(name = "black", type = boolean.class)
+    })
+    static class ColumnModel {
+    }
+
+    @Test public void deepClone() {
+        Board orig = new Board(new Row(new Column(true)));
+        assertTrue(orig.getRows().get(0).getColumn().get(0).isBlack(), "Really true");
+        
+        Board clone = orig.clone();
+        assertTrue(clone.getRows().get(0).getColumn().get(0).isBlack(), "Clone also true");
+        
+        clone.getRows().get(0).getColumn().get(0).setBlack(false);
+        
+        assertFalse(clone.getRows().get(0).getColumn().get(0).isBlack(), "Clone also is not false");
+        assertTrue(orig.getRows().get(0).getColumn().get(0).isBlack(), "Orig still true");
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/net/java/html/json/BooleanArrayTest.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/net/java/html/json/BooleanArrayTest.java b/json/src/test/java/net/java/html/json/BooleanArrayTest.java
new file mode 100644
index 0000000..78c134d
--- /dev/null
+++ b/json/src/test/java/net/java/html/json/BooleanArrayTest.java
@@ -0,0 +1,80 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json;
+
+import java.util.Collections;
+import java.util.List;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+@Model(className="BooleanArray", builder = "put", properties = {
+    @Property(name = "array", type = boolean.class, array = true)
+})
+public class BooleanArrayTest {
+    @ComputedProperty static int length(List<Boolean> array) {
+        return array.size();
+    }
+
+    @ComputedProperty static List<Integer> lengthAsList(List<Boolean> array) {
+        return Collections.nCopies(1, array.size());
+    }
+
+    @ComputedProperty static List<String> lengthTextList(List<Boolean> array) {
+        return Collections.nCopies(1, "" + array.size());
+    }
+    
+    @Test public void generatedConstructorWithPrimitiveType() {
+        boolean[] arr = new boolean[10];
+        arr[3] = true;
+        BooleanArray a = new BooleanArray().putArray(arr);
+        Assert.assertEquals(a.getArray().size(), 10, "Ten elements");
+        Assert.assertEquals(a.getArray().get(3).booleanValue(), true, "Value ten");
+        Assert.assertEquals(a.getLength(), 10, "Derived property is OK too");
+        Assert.assertEquals(a.getLengthTextList().get(0), "10", "Derived string list property is OK");
+        Assert.assertEquals((int)a.getLengthAsList().get(0), 10, "Derived Integer list property is OK");
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/net/java/html/json/Compile.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/net/java/html/json/Compile.java b/json/src/test/java/net/java/html/json/Compile.java
new file mode 100644
index 0000000..13b812e
--- /dev/null
+++ b/json/src/test/java/net/java/html/json/Compile.java
@@ -0,0 +1,311 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.tools.Diagnostic;
+import javax.tools.DiagnosticListener;
+import javax.tools.FileObject;
+import javax.tools.ForwardingJavaFileManager;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+import javax.tools.JavaFileObject.Kind;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.StandardLocation;
+import javax.tools.ToolProvider;
+import static org.testng.Assert.*;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+final class Compile implements DiagnosticListener<JavaFileObject> {
+    private final List<Diagnostic<? extends JavaFileObject>> errors = 
+            new ArrayList<Diagnostic<? extends JavaFileObject>>();
+    private final Map<String, byte[]> classes;
+    private final String pkg;
+    private final String cls;
+    private final String html;
+    private final String sourceLevel;
+
+    private Compile(String html, String code, String sl) throws IOException {
+        this.pkg = findPkg(code);
+        this.cls = findCls(code);
+        this.html = html;
+        this.sourceLevel = sl;
+        classes = compile(html, code);
+    }
+
+    /** Performs compilation of given HTML page and associated Java code
+     */
+    public static Compile create(String html, String code) throws IOException {
+        return create(html, code, "1.7");
+    }
+    static Compile create(String html, String code, String sourceLevel) throws IOException {
+        return new Compile(html, code, sourceLevel);
+    }
+    
+    /** Checks for given class among compiled resources */
+    public byte[] get(String res) {
+        return classes.get(res);
+    }
+    
+    /** Obtains errors created during compilation.
+     */
+    public List<Diagnostic<? extends JavaFileObject>> getErrors() {
+        List<Diagnostic<? extends JavaFileObject>> err;
+        err = new ArrayList<Diagnostic<? extends JavaFileObject>>();
+        for (Diagnostic<? extends JavaFileObject> diagnostic : errors) {
+            if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
+                err.add(diagnostic);
+            }
+        }
+        return err;
+    }
+    
+    private Map<String, byte[]> compile(final String html, final String code) throws IOException {
+        StandardJavaFileManager sjfm = ToolProvider.getSystemJavaCompiler().getStandardFileManager(this, null, null);
+
+        final Map<String, ByteArrayOutputStream> class2BAOS;
+        class2BAOS = new HashMap<String, ByteArrayOutputStream>();
+
+        JavaFileObject file = new SimpleJavaFileObject(URI.create("mem://mem"), Kind.SOURCE) {
+            @Override
+            public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+                return code;
+            }
+        };
+        final JavaFileObject htmlFile = new SimpleJavaFileObject(URI.create("mem://mem2"), Kind.OTHER) {
+            @Override
+            public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+                return html;
+            }
+
+            @Override
+            public InputStream openInputStream() throws IOException {
+                return new ByteArrayInputStream(html.getBytes());
+            }
+        };
+        
+        final URI scratch;
+        try {
+            scratch = new URI("mem://mem3");
+        } catch (URISyntaxException ex) {
+            throw new IOException(ex);
+        }
+        
+        JavaFileManager jfm = new ForwardingJavaFileManager<JavaFileManager>(sjfm) {
+            @Override
+            public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) throws IOException {
+                if (kind  == Kind.CLASS) {
+                    final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+
+                    class2BAOS.put(className.replace('.', '/') + ".class", buffer);
+                    return new SimpleJavaFileObject(sibling.toUri(), kind) {
+                        @Override
+                        public OutputStream openOutputStream() throws IOException {
+                            return buffer;
+                        }
+                    };
+                }
+                
+                if (kind == Kind.SOURCE) {
+                    final String n = className.replace('.', '/') + ".java";
+                    final URI un;
+                    try {
+                        un = new URI("mem://" + n);
+                    } catch (URISyntaxException ex) {
+                        throw new IOException(ex);
+                    }
+                    return new VirtFO(un/*sibling.toUri()*/, kind, n);
+                }
+                
+                throw new IllegalStateException();
+            }
+
+            @Override
+            public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException {
+                if (location == StandardLocation.SOURCE_PATH) {
+                    if (packageName.equals(pkg)) {
+                        return htmlFile;
+                    }
+                }
+                
+                return null;
+            }
+
+            @Override
+            public boolean isSameFile(FileObject a, FileObject b) {
+                if (a instanceof VirtFO && b instanceof VirtFO) {
+                    return ((VirtFO)a).getName().equals(((VirtFO)b).getName());
+                }
+                
+                return super.isSameFile(a, b);
+            }
+
+            class VirtFO extends SimpleJavaFileObject {
+
+                private final String n;
+
+                public VirtFO(URI uri, Kind kind, String n) {
+                    super(uri, kind);
+                    this.n = n;
+                }
+                private final ByteArrayOutputStream data = new ByteArrayOutputStream() {
+
+                    @Override
+                    public void close() throws IOException {
+                        super.close();
+
+                        int opening = count(toString(), '{');
+                        int closing = count(toString(), '}');
+
+                        assertEquals(opening, closing, "There should be pair number of { and } in\n" + toString());
+                    }
+                };
+
+                int count(String where, char what) {
+                    int at = -1;
+                    int cnt = 0;
+                    for (;;) {
+                        at = where.indexOf(what, at + 1);
+                        if (at == -1) {
+                            return cnt;
+                        }
+                        cnt++;
+                    }
+                }
+
+                @Override
+                public OutputStream openOutputStream() throws IOException {
+                    return data;
+                }
+
+                @Override
+                public String getName() {
+                    return n;
+                }
+
+                @Override
+                public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+                    data.close();
+                    return new String(data.toByteArray());
+                }
+            }
+        };
+
+        ToolProvider.getSystemJavaCompiler().getTask(null, jfm, this, /*XXX:*/Arrays.asList("-source", sourceLevel, "-target", "1.7"), null, Arrays.asList(file)).call();
+
+        Map<String, byte[]> result = new HashMap<String, byte[]>();
+
+        for (Map.Entry<String, ByteArrayOutputStream> e : class2BAOS.entrySet()) {
+            result.put(e.getKey(), e.getValue().toByteArray());
+        }
+
+        return result;
+    }
+
+
+    @Override
+    public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
+        errors.add(diagnostic);
+    }
+    private static String findPkg(String java) throws IOException {
+        Pattern p = Pattern.compile("package\\p{javaWhitespace}*([\\p{Alnum}\\.]+)\\p{javaWhitespace}*;", Pattern.MULTILINE);
+        Matcher m = p.matcher(java);
+        if (!m.find()) {
+            throw new IOException("Can't find package declaration in the java file");
+        }
+        String pkg = m.group(1);
+        return pkg;
+    }
+    private static String findCls(String java) throws IOException {
+        Pattern p = Pattern.compile("class\\p{javaWhitespace}*([\\p{Alnum}\\.]+)\\p{javaWhitespace}", Pattern.MULTILINE);
+        Matcher m = p.matcher(java);
+        if (!m.find()) {
+            throw new IOException("Can't find package declaration in the java file");
+        }
+        String cls = m.group(1);
+        return cls;
+    }
+
+    String getHtml() {
+        String fqn = "'" + pkg + '.' + cls + "'";
+        return html.replace("'${fqn}'", fqn);
+    }
+    void assertErrors() {
+        assertFalse(getErrors().isEmpty(), "There are supposed to be some errors");
+    }
+    void assertNoErrors() {
+        assertTrue(getErrors().isEmpty(), "There are supposed to be no errors: " + getErrors());
+    }
+
+    void assertError(String expMsg) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("Can't find ").append(expMsg).append(" among:");
+        for (Diagnostic<? extends JavaFileObject> e : errors) {
+            String msg = e.getMessage(Locale.US);
+            if (msg.contains(expMsg)) {
+                return;
+            }
+            sb.append("\n");
+            sb.append(msg);
+        }
+        fail(sb.toString());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/net/java/html/json/KeywordsTest.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/net/java/html/json/KeywordsTest.java b/json/src/test/java/net/java/html/json/KeywordsTest.java
new file mode 100644
index 0000000..9e79895
--- /dev/null
+++ b/json/src/test/java/net/java/html/json/KeywordsTest.java
@@ -0,0 +1,99 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json;
+
+import static org.testng.Assert.*;
+import org.testng.annotations.Test;
+
+@Model(className = "Keywords", properties = {
+    @Property(name = "private", type = String.class),
+    @Property(name = "public", type = double.class),
+    @Property(name = "final", type = String.class),
+    @Property(name = "int", type = int.class),
+    @Property(name = "class", type = String.class),
+//    @Property(name = "{", type = String.class),
+    @Property(name = "array", type = KeywordsInArray.class)
+})
+public class KeywordsTest {
+    @Model(className = "KeywordsInArray", properties = {
+        @Property(name = "private", type = String.class, array = true),
+        @Property(name = "public", type = double.class, array = true),
+        @Property(name = "final", type = String.class, array = true),
+        @Property(name = "int", type = int.class, array = true),
+        @Property(name = "class", type = String.class, array = true),
+//    @Property(name = "{", type = String.class),
+        @Property(name = "array", type = Keywords.class, array = true)
+    })
+    static class KeywordsInArrayCntrl {
+    }
+    
+    @Test public void verifyKeywordsClassCompiles() {
+        Keywords k = new Keywords();
+        k.setClass("c");
+        k.setFinal("f");
+        k.setInt(10);
+        k.setPrivate("p");
+        k.setPublic(42.0);
+        
+        assertEquals(k.accessClass(), "c");
+        assertEquals(k.getFinal(), "f");
+        assertEquals(k.getInt(), 10);
+        assertEquals(k.getPrivate(), "p");
+        assertEquals(k.getPublic(), 42.0);
+    }
+    
+    @Test public void verifyKeywordsInArrayClassCompiles() {
+        KeywordsInArray k = new KeywordsInArray();
+        k.accessClass().add("c");
+        k.getFinal().add("f");
+        k.getInt().add(10);
+        k.getPrivate().add("p");
+        k.getPublic().add(42.0);
+        
+        assertEquals(k.accessClass().get(0), "c");
+        assertEquals(k.getFinal().get(0), "f");
+        assertEquals(k.getInt().get(0), Integer.valueOf(10));
+        assertEquals(k.getPrivate().get(0), "p");
+        assertEquals(k.getPublic().get(0), 42.0);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/net/java/html/json/MapModelNotMutableTest.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/net/java/html/json/MapModelNotMutableTest.java b/json/src/test/java/net/java/html/json/MapModelNotMutableTest.java
new file mode 100644
index 0000000..4851c5a
--- /dev/null
+++ b/json/src/test/java/net/java/html/json/MapModelNotMutableTest.java
@@ -0,0 +1,227 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json;
+
+import java.util.Map;
+import net.java.html.BrwsrCtx;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.json.spi.Technology;
+import org.netbeans.html.json.spi.Transfer;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.fail;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+@Model(className = "ConstantValues", properties = {
+    @Property(name = "byteNumber", type = byte.class, mutable = false),
+    @Property(name = "shortNumber", type = short.class, mutable = false),
+    @Property(name = "intNumber", type = int.class, mutable = false),
+    @Property(name = "longNumber", type = long.class, mutable = false),
+    @Property(name = "floatNumber", type = float.class, mutable = false),
+    @Property(name = "doubleNumber", type = double.class, mutable = false),
+    @Property(name = "stringValue", type = String.class, mutable = false),
+    @Property(name = "byteArray", type = byte.class, mutable = false, array = true),
+    @Property(name = "shortArray", type = short.class, mutable = false, array = true),
+    @Property(name = "intArray", type = int.class, mutable = false, array = true),
+    @Property(name = "longArray", type = long.class, mutable = false, array = true),
+    @Property(name = "floatArray", type = float.class, mutable = false, array = true),
+    @Property(name = "doubleArray", type = double.class, mutable = false, array = true),
+    @Property(name = "stringArray", type = String.class, mutable = false, array = true),
+})
+public class MapModelNotMutableTest {
+    private BrwsrCtx c;
+
+    @BeforeMethod
+    public void initTechnology() {
+        MapModelTest.MapTechnology t = new MapModelTest.MapTechnology();
+        c = Contexts.newBuilder().register(Technology.class, t, 1).
+            register(Transfer.class, t, 1).build();
+    }
+
+    @Test
+    public void byteConstant() throws Exception {
+        ConstantValues value = Models.bind(new ConstantValues(), c);
+        value.setByteNumber((byte)13);
+
+        Map m = (Map) Models.toRaw(value);
+        Object v = m.get("byteNumber");
+        assertNotNull(v, "Value should be in the map");
+        assertEquals(v.getClass(), MapModelTest.One.class, "It is instance of One");
+        MapModelTest.One o = (MapModelTest.One) v;
+        assertEquals(o.changes, 0, "No change so far the only one change happened before we connected");
+        assertEquals((byte)13, o.get());
+
+        try {
+            value.setByteNumber((byte)15);
+            fail("Changing value shouldn't succeed!");
+        } catch (IllegalStateException ex) {
+            // OK
+        }
+        assertEquals(o.get(), (byte)13, "Old value should still be in the map");
+        assertEquals(o.changes, 0, "No change");
+        assertFalse(o.pb.isReadOnly(), "Mutable property");
+    }
+
+    @Test
+    public void shortConstant() throws Exception {
+        ConstantValues value = Models.bind(new ConstantValues(), c);
+        value.setShortNumber((short)13);
+
+        Map m = (Map) Models.toRaw(value);
+        Object v = m.get("shortNumber");
+        assertNotNull(v, "Value should be in the map");
+        assertEquals(v.getClass(), MapModelTest.One.class, "It is instance of One");
+        MapModelTest.One o = (MapModelTest.One) v;
+        assertEquals(o.changes, 0, "No change so far the only one change happened before we connected");
+        assertEquals((short)13, o.get());
+
+        try {
+            value.setShortNumber((short)15);
+            fail("Changing value shouldn't succeed!");
+        } catch (IllegalStateException ex) {
+            // OK
+        }
+        assertEquals(o.get(), (short)13, "Old value should still be in the map");
+        assertEquals(o.changes, 0, "No change");
+        assertFalse(o.pb.isReadOnly(), "Mutable property");
+    }
+
+    @Test
+    public void intConstant() throws Exception {
+        ConstantValues value = Models.bind(new ConstantValues(), c);
+        value.setIntNumber(13);
+
+        Map m = (Map) Models.toRaw(value);
+        Object v = m.get("intNumber");
+        assertNotNull(v, "Value should be in the map");
+        assertEquals(v.getClass(), MapModelTest.One.class, "It is instance of One");
+        MapModelTest.One o = (MapModelTest.One) v;
+        assertEquals(o.changes, 0, "No change so far the only one change happened before we connected");
+        assertEquals(13, o.get());
+
+        try {
+            value.setIntNumber(15);
+            fail("Changing value shouldn't succeed!");
+        } catch (IllegalStateException ex) {
+            // OK
+        }
+        assertEquals(o.get(), 13, "Old value should still be in the map");
+        assertEquals(o.changes, 0, "No change");
+        assertFalse(o.pb.isReadOnly(), "Mutable property");
+    }
+
+    @Test
+    public void doubleConstant() throws Exception {
+        ConstantValues value = Models.bind(new ConstantValues(), c);
+        value.setDoubleNumber(13);
+
+        Map m = (Map) Models.toRaw(value);
+        Object v = m.get("doubleNumber");
+        assertNotNull(v, "Value should be in the map");
+        assertEquals(v.getClass(), MapModelTest.One.class, "It is instance of One");
+        MapModelTest.One o = (MapModelTest.One) v;
+        assertEquals(o.changes, 0, "No change so far the only one change happened before we connected");
+        assertEquals(13.0, o.get());
+
+        try {
+            value.setDoubleNumber(15);
+            fail("Changing value shouldn't succeed!");
+        } catch (IllegalStateException ex) {
+            // OK
+        }
+        assertEquals(o.get(), 13.0, "Old value should still be in the map");
+        assertEquals(o.changes, 0, "No change");
+        assertFalse(o.pb.isReadOnly(), "Mutable property");
+    }
+
+    @Test
+    public void stringConstant() throws Exception {
+        ConstantValues value = Models.bind(new ConstantValues(), c);
+        value.setStringValue("Hi");
+
+        Map m = (Map) Models.toRaw(value);
+        Object v = m.get("stringValue");
+        assertNotNull(v, "Value should be in the map");
+        assertEquals(v.getClass(), MapModelTest.One.class, "It is instance of One");
+        MapModelTest.One o = (MapModelTest.One) v;
+        assertEquals(o.changes, 0, "No change so far the only one change happened before we connected");
+        assertEquals("Hi", o.get());
+
+        try {
+            value.setStringValue("Hello");
+            fail("Changing value shouldn't succeed!");
+        } catch (IllegalStateException ex) {
+            // OK
+        }
+        assertEquals(o.get(), "Hi", "Old value should still be in the map");
+        assertEquals(o.changes, 0, "No change");
+        assertFalse(o.pb.isReadOnly(), "Mutable property");
+    }
+
+    @Test
+    public void stringArray() throws Exception {
+        ConstantValues value = Models.bind(new ConstantValues(), c);
+        value.getStringArray().add("Hi");
+
+        Map m = (Map) Models.toRaw(value);
+        Object v = m.get("stringArray");
+        assertNotNull(v, "Value should be in the map");
+        assertEquals(v.getClass(), MapModelTest.One.class, "It is instance of One");
+        MapModelTest.One o = (MapModelTest.One) v;
+        assertEquals(o.changes, 0, "No change so far the only one change happened before we connected");
+        assertEquals(o.get(), new String[] { "Hi" }, "One element");
+
+        try {
+            value.getStringArray().add("Hello");
+            fail("Changing value shouldn't succeed!");
+        } catch (UnsupportedOperationException ex) {
+            // OK
+        }
+        assertEquals(o.get(), new String[] { "Hi" }, "Old value should still be in the map");
+        assertEquals(o.changes, 0, "No change");
+        assertFalse(o.pb.isReadOnly(), "Mutable property");
+    }
+
+}


[09/24] incubator-netbeans-html4j git commit: [INFRA-15006] Initial donation of HTML/Java NetBeans API. Equivalent to the content of html4j-donation-review.zip donated as part of ApacheNetBeansDonation1.zip with SHA256 being 7f2ca0f61953a190613c9a0fbcc1b

Posted by jt...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/main/java/org/netbeans/html/json/impl/PropertyBindingAccessor.java
----------------------------------------------------------------------
diff --git a/json/src/main/java/org/netbeans/html/json/impl/PropertyBindingAccessor.java b/json/src/main/java/org/netbeans/html/json/impl/PropertyBindingAccessor.java
new file mode 100644
index 0000000..bf545fa
--- /dev/null
+++ b/json/src/main/java/org/netbeans/html/json/impl/PropertyBindingAccessor.java
@@ -0,0 +1,109 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.json.impl;
+
+import net.java.html.BrwsrCtx;
+import org.netbeans.html.json.spi.JSONCall;
+import org.netbeans.html.json.spi.PropertyBinding;
+import org.netbeans.html.json.spi.Proto;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public abstract class PropertyBindingAccessor {
+    private static PropertyBindingAccessor DEFAULT;
+
+    protected PropertyBindingAccessor() {
+        if (DEFAULT != null) throw new IllegalStateException();
+        DEFAULT = this;
+    }
+
+    static {
+        JSON.initClass(PropertyBinding.class);
+    }
+
+    protected abstract <M> PropertyBinding newBinding(
+        Proto.Type<M> access, Bindings<?> bindings, String name, int index, M model, byte propertyType);
+    protected abstract JSONCall newCall(
+        BrwsrCtx ctx, RcvrJSON callback,
+        String headers, String urlBefore, String urlAfter,
+        String method, Object data
+    );
+
+    protected abstract Bindings bindings(Proto proto, boolean initialize, Object copyFrom);
+    protected abstract void notifyChange(Proto proto, int propIndex);
+    protected abstract Proto findProto(Proto.Type<?> type, Object object);
+    protected abstract <Model> Model cloneTo(Proto.Type<Model> type, Model model, BrwsrCtx c);
+    protected abstract Object read(Proto.Type<?> from, BrwsrCtx c, Object data);
+
+    static Bindings getBindings(Proto proto, boolean initialize, Object copyFrom) {
+        return DEFAULT.bindings(proto, initialize, copyFrom);
+    }
+
+    static void notifyProtoChange(Proto proto, int propIndex) {
+        DEFAULT.notifyChange(proto, propIndex);
+    }
+
+    static <M> PropertyBinding create(
+        Proto.Type<M> access, Bindings<?> bindings, String name, int index, M model , byte propertyType
+    ) {
+        return DEFAULT.newBinding(access, bindings, name, index, model, propertyType);
+    }
+    public static JSONCall createCall(
+        BrwsrCtx ctx, RcvrJSON callback,
+        String headers, String urlBefore, String urlAfter,
+        String method, Object data
+    ) {
+        return DEFAULT.newCall(ctx, callback, headers, urlBefore, urlAfter, method, data);
+    }
+    static Proto protoFor(Proto.Type<?> type, Object object) {
+        return DEFAULT.findProto(type, object);
+    }
+    static <Model> Model clone(Proto.Type<Model> type, Model model, BrwsrCtx c) {
+        return DEFAULT.cloneTo(type, model, c);
+    }
+    static Object readFrom(Proto.Type<?> from, BrwsrCtx c, Object data) {
+        return DEFAULT.read(from, c, data);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/main/java/org/netbeans/html/json/impl/RcvrJSON.java
----------------------------------------------------------------------
diff --git a/json/src/main/java/org/netbeans/html/json/impl/RcvrJSON.java b/json/src/main/java/org/netbeans/html/json/impl/RcvrJSON.java
new file mode 100644
index 0000000..d3764f9
--- /dev/null
+++ b/json/src/main/java/org/netbeans/html/json/impl/RcvrJSON.java
@@ -0,0 +1,136 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.json.impl;
+
+import java.util.concurrent.Callable;
+
+/** Super type for those who wish to receive JSON messages.
+ *
+ * @author Jaroslav Tulach
+ */
+public abstract class RcvrJSON {
+    protected void onOpen(MsgEvnt msg) {}
+    protected abstract void onMessage(MsgEvnt msg);
+    protected void onClose(MsgEvnt msg) {}
+    protected abstract void onError(MsgEvnt msg);
+    
+    public abstract static class MsgEvnt {
+        MsgEvnt() {
+        }
+        
+        public Throwable getError() {
+            return null;
+        }
+        
+        public final Exception getException() {
+            Throwable t = getError();
+            if (t instanceof Exception) {
+                return (Exception)t;
+            }
+            if (t == null) {
+                return null;
+            }
+            return new Exception(t);
+        }
+        
+        public Object[] getValues() {
+            return null;
+        }
+        
+        public abstract void dispatch(RcvrJSON r);
+        
+        public static MsgEvnt createError(final Throwable t) {
+            return new MsgEvnt() {
+                @Override
+                public Throwable getError() {
+                    return t;
+                }
+
+                @Override
+                public void dispatch(RcvrJSON r) {
+                    r.onError(this);
+                }
+            };
+        }
+        
+         public static MsgEvnt createMessage(final Object value) {
+            return new MsgEvnt() {
+                private Object val = value;
+
+                @Override
+                public Object[] getValues() {
+                    if (val instanceof Callable) {
+                        try {
+                            val = ((Callable)val).call();
+                        } catch (Exception ex) {
+                            throw new IllegalStateException("Cannot compute " + val, ex);
+                        }
+                    }
+                    return val instanceof Object[] ? (Object[])val : new Object[] { val };
+                }
+                
+                @Override
+                public void dispatch(RcvrJSON r) {
+                    r.onMessage(this);
+                }
+            };
+        }
+        
+        public static MsgEvnt createOpen() {
+            return new MsgEvnt() {
+                @Override
+                public void dispatch(RcvrJSON r) {
+                    r.onOpen(this);
+                }
+            };
+        }
+
+        public static MsgEvnt createClose() {
+            return new MsgEvnt() {
+                @Override
+                public void dispatch(RcvrJSON r) {
+                    r.onClose(this);
+                }
+            };
+        }
+    } }

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/main/java/org/netbeans/html/json/impl/Transitive.java
----------------------------------------------------------------------
diff --git a/json/src/main/java/org/netbeans/html/json/impl/Transitive.java b/json/src/main/java/org/netbeans/html/json/impl/Transitive.java
new file mode 100644
index 0000000..e7713ed
--- /dev/null
+++ b/json/src/main/java/org/netbeans/html/json/impl/Transitive.java
@@ -0,0 +1,60 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.json.impl;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** A way to control {@link ComputedProperty} behavior - whether it tracks
+ * deeply or not. Not public yet, maybe it won't be needed in the API at all.
+ * Used in tests only.
+ * 
+ * @author Jaroslav Tulach
+ */
+@Retention(RetentionPolicy.SOURCE)
+@Target(ElementType.METHOD)
+@interface Transitive {
+    boolean deep() default false;
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/main/java/org/netbeans/html/json/spi/FunctionBinding.java
----------------------------------------------------------------------
diff --git a/json/src/main/java/org/netbeans/html/json/spi/FunctionBinding.java b/json/src/main/java/org/netbeans/html/json/spi/FunctionBinding.java
new file mode 100644
index 0000000..10bdc53
--- /dev/null
+++ b/json/src/main/java/org/netbeans/html/json/spi/FunctionBinding.java
@@ -0,0 +1,162 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.json.spi;
+
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import net.java.html.BrwsrCtx;
+import net.java.html.json.Function;
+import net.java.html.json.Model;
+
+/** Describes a function provided by the {@link Model} and 
+ * annotated by {@link Function} annotation.
+ *
+ * @author Jaroslav Tulach
+ */
+public abstract class FunctionBinding {
+    FunctionBinding() {
+    }
+    
+    /** Returns name of the function.
+     * @return function name
+     */
+    public abstract String getFunctionName();
+    
+    /**
+     * Calls the function provided data associated with current element, as well
+     * as information about the event that triggered the event.
+     *
+     * @param data data associated with selected element
+     * @param ev event (with additional properties) that triggered the event
+     */
+    public abstract void call(Object data, Object ev);
+    
+    /** Returns identical version of the binding, but one that holds on the
+     * original model object via weak reference.
+     * 
+     * @return binding that uses weak reference
+     * @since 1.1
+     */
+    public abstract FunctionBinding weak();
+
+    static <M> FunctionBinding registerFunction(String name, int index, M model, Proto.Type<M> access) {
+        return new Impl<M>(model, name, index, access);
+    }
+    
+    private static abstract class AImpl<M> extends FunctionBinding {
+        final String name;
+        final Proto.Type<M> access;
+        final int index;
+
+        public AImpl(String name, int index, Proto.Type<M> access) {
+            this.name = name;
+            this.index = index;
+            this.access = access;
+        }
+        
+        protected abstract M model();
+
+        @Override
+        public String getFunctionName() {
+            return name;
+        }
+
+        @Override
+        public void call(final Object data, final Object ev) {
+            final M model = model();
+            if (model == null) {
+                return;
+            }
+            BrwsrCtx ctx = access.protoFor(model).getContext();
+            class Dispatch implements Runnable {
+                @Override
+                public void run() {
+                    try {
+                        access.call(model, index, data, ev);
+                    } catch (Throwable ex) {
+                        ex.printStackTrace();
+                    }
+                }
+            }
+            ctx.execute(new Dispatch());
+        }
+    }
+    
+    private static final class Impl<M> extends AImpl<M> {
+        private final M model;
+
+        public Impl(M model, String name, int index, Proto.Type<M> access) {
+            super(name, index, access);
+            this.model = model;
+        }
+
+        @Override
+        protected M model() {
+            return model;
+        }
+
+        @Override
+        public FunctionBinding weak() {
+            return new Weak(model, name, index, access);
+        }
+    }
+    
+    private static final class Weak<M> extends AImpl<M> {
+        private final Reference<M> ref;
+        
+        public Weak(M model, String name, int index, Proto.Type<M> access) {
+            super(name, index, access);
+            this.ref = new WeakReference<M>(model);
+        }
+
+        @Override
+        protected M model() {
+            return ref.get();
+        }
+
+        @Override
+        public FunctionBinding weak() {
+            return this;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/main/java/org/netbeans/html/json/spi/JSONCall.java
----------------------------------------------------------------------
diff --git a/json/src/main/java/org/netbeans/html/json/spi/JSONCall.java b/json/src/main/java/org/netbeans/html/json/spi/JSONCall.java
new file mode 100644
index 0000000..bf1a649
--- /dev/null
+++ b/json/src/main/java/org/netbeans/html/json/spi/JSONCall.java
@@ -0,0 +1,152 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.json.spi;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import net.java.html.BrwsrCtx;
+import org.netbeans.html.json.impl.RcvrJSON;
+
+/** Description of a JSON call request that is supposed to be processed
+ * by {@link Transfer#loadJSON(org.netbeans.html.json.spi.JSONCall)} implementors.
+ *
+ * @author Jaroslav Tulach
+ */
+public final class JSONCall {
+    private final RcvrJSON whenDone;
+    private final String headers;
+    private final String urlBefore;
+    private final String urlAfter;
+    private final String method;
+    private final Object data;
+    private final BrwsrCtx ctx;
+
+    JSONCall(
+        BrwsrCtx ctx, RcvrJSON whenDone,
+        String headers, String urlBefore, String urlAfter,
+        String method, Object data
+    ) {
+        this.ctx = ctx;
+        this.whenDone = whenDone;
+        this.headers = headers;
+        this.urlBefore = urlBefore;
+        this.urlAfter = urlAfter;
+        this.method = method;
+        this.data = data;
+    }
+
+    /** Do we have some data to send? Can the {@link #writeData(java.io.OutputStream)} method be
+     * called?
+     *
+     * @return true, if the call has some data to send
+     */
+    public boolean isDoOutput() {
+        return this.data != null;
+    }
+
+    public void writeData(OutputStream os) throws IOException {
+        if (this.data == null) {
+            throw new IOException("No data!");
+        }
+        os.write(this.data.toString().getBytes("UTF-8"));
+        os.flush();
+    }
+
+    /** Additional headers to be included in the request.
+     * Usually multiline string to be appended into the header.
+     *
+     * @return <code>null</code> or string with prepared (HTTP) request headers
+     * @since 1.2
+     */
+    public String getHeaders() {
+        return headers;
+    }
+
+    public String getMethod() {
+        return method;
+    }
+
+    public boolean isJSONP() {
+        return urlAfter != null;
+    }
+
+    public String composeURL(String jsonpCallback) {
+        if ((urlAfter == null) != (jsonpCallback == null)) {
+            throw new IllegalStateException();
+        }
+        if (urlAfter != null) {
+            return urlBefore + jsonpCallback + urlAfter;
+        } else {
+            return urlBefore;
+        }
+    }
+
+    public void notifySuccess(Object result) {
+        if (result == null) {
+            dispatch(RcvrJSON.MsgEvnt.createOpen());
+        } else {
+            dispatch(RcvrJSON.MsgEvnt.createMessage(result));
+        }
+    }
+
+    public void notifyError(Throwable error) {
+        if (error == null) {
+            dispatch(RcvrJSON.MsgEvnt.createClose());
+        } else {
+            dispatch(RcvrJSON.MsgEvnt.createError(error));
+        }
+    }
+
+    private void dispatch(final RcvrJSON.MsgEvnt ev) {
+        ctx.execute(new Runnable() {
+            @Override
+            public void run() {
+                ev.dispatch(whenDone);
+            }
+        });
+    }
+
+    public String getMessage() {
+        return this.data.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/main/java/org/netbeans/html/json/spi/Observers.java
----------------------------------------------------------------------
diff --git a/json/src/main/java/org/netbeans/html/json/spi/Observers.java b/json/src/main/java/org/netbeans/html/json/spi/Observers.java
new file mode 100644
index 0000000..cb12ac1
--- /dev/null
+++ b/json/src/main/java/org/netbeans/html/json/spi/Observers.java
@@ -0,0 +1,233 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.json.spi;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+final class Observers {
+    private static final LinkedList<Watcher> GLOBAL = new LinkedList<Watcher>();
+    private final List<Watcher> watchers = new ArrayList<Watcher>();
+    private final List<Ref> observers = new ArrayList<Ref>();
+
+    Observers() {
+        assert Thread.holdsLock(GLOBAL);
+    }
+    
+    static void beginComputing(Proto p, String name) {
+        synchronized (GLOBAL) {
+            verifyUnlocked(p);
+            final Watcher nw = new Watcher(p, name);
+            GLOBAL.push(nw);
+        }
+    }
+    
+    static void verifyUnlocked(Proto p) {
+        synchronized (GLOBAL) {
+            for (Watcher w : GLOBAL) {
+                if (w.proto == p) {
+                    if (w.owner == Thread.currentThread()) {
+                        throw new IllegalStateException("Re-entrant attempt to access " + p);
+                    }
+                }
+            }
+        }        
+    }
+
+    static void accessingValue(Proto p, String propName) {
+        synchronized (GLOBAL) {
+            verifyUnlocked(p);
+            for (Watcher w : GLOBAL) {
+                Observers mine = p.observers(true);
+                mine.add(w, new Ref(w, propName));
+            }
+        }
+    }
+    
+    static void finishComputing(Proto p) {
+        synchronized (GLOBAL) {
+            boolean found = false;
+            Iterator<Watcher> it = GLOBAL.iterator();
+            while (it.hasNext()) {
+                Watcher w = it.next();
+                if (w.proto == p && w.owner == Thread.currentThread()) {
+                    if (w.prop != null) {
+                        Observers mine = p.observers(true);
+                        mine.add(w);
+                    }
+                    found = true;
+                    it.remove();
+                }
+            }
+            if (!found) {
+                throw new IllegalStateException("Cannot find " + p + " in " + GLOBAL);
+            }
+        }
+    }
+    
+    private static final class Ref extends WeakReference<Watcher> {
+        private final String prop;
+        
+        public Ref(Watcher ref, String prop) {
+            super(ref);
+            this.prop = prop;
+        }
+        
+        final Watcher watcher() {
+            Watcher w = get();
+            if (w == null) {
+                return null;
+            }
+            final Observers o = w.proto.observers(false);
+            if (o == null) {
+                return null;
+            }
+            if (o.find(w.prop) == w) {
+                return w;
+            }
+            return null;
+        }
+    }
+    
+    private Watcher find(String prop) {
+        if (prop == null) {
+            return null;
+        }
+        for (Watcher w : watchers) {
+            if (prop.equals(w.prop)) {
+                return w;
+            }
+        }
+        return null;
+    }
+
+        final void add(Watcher w) {
+        for (int i = 0; i < watchers.size(); i++) {
+            Watcher ith = watchers.get(i);
+            if (w.prop == null) {
+                if (ith.prop == null) {
+                    watchers.set(i, w);
+                    return;
+                }
+            } else if (w.prop.equals(ith.prop)) {
+                watchers.set(i, w);
+                return;
+            }
+        }
+        watchers.add(w);
+    }
+
+    static final void valueHasMutated(Proto p, String propName) {
+        List<Watcher> mutated = new LinkedList<Watcher>();
+        synchronized (GLOBAL) {
+            Observers mine = p.observers(false);
+            if (mine == null) {
+                return;
+            }
+            Iterator<Ref> it = mine.observers.iterator();
+            while (it.hasNext()) {
+                Ref ref = it.next();
+                if (ref.get() == null) {
+                    it.remove();
+                    continue;
+                }
+                if (ref.prop.equals(propName)) {
+                    Watcher w = ref.watcher();
+                    if (w != null) {
+                        mutated.add(w);
+                    }
+                }
+            }
+        }
+        for (Watcher w : mutated) {
+            w.proto.valueHasMutated(w.prop);
+        }
+    }
+
+    void add(Watcher w, Ref r) {
+        Thread.holdsLock(GLOBAL);
+        if (w == null) {
+            return;
+        }
+        Iterator<Ref> it = observers.iterator();
+        while (it.hasNext()) {
+            Ref ref = it.next();
+            if (r == ref) {
+                return;
+            }
+            final Watcher rw = ref.get();
+            if (rw == null) {
+                it.remove();
+                continue;
+            }
+            if (rw == w && r.prop.equals(r.prop)) {
+                return;
+            }
+        }
+        observers.add(r);
+    }
+    
+    private static final class Watcher {
+        final Thread owner;
+        final Proto proto;
+        final String prop;
+
+        Watcher(Proto proto, String prop) {
+            this.owner = Thread.currentThread();
+            this.proto = proto;
+            this.prop = prop;
+        }
+        
+        @Override
+        public String toString() {
+            return "Watcher: " + proto + ", " + prop;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/main/java/org/netbeans/html/json/spi/PropertyBinding.java
----------------------------------------------------------------------
diff --git a/json/src/main/java/org/netbeans/html/json/spi/PropertyBinding.java b/json/src/main/java/org/netbeans/html/json/spi/PropertyBinding.java
new file mode 100644
index 0000000..c43217d
--- /dev/null
+++ b/json/src/main/java/org/netbeans/html/json/spi/PropertyBinding.java
@@ -0,0 +1,236 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.json.spi;
+
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import net.java.html.BrwsrCtx;
+import net.java.html.json.ComputedProperty;
+import org.netbeans.html.json.impl.Bindings;
+import org.netbeans.html.json.impl.JSON;
+import org.netbeans.html.json.impl.PropertyBindingAccessor;
+import org.netbeans.html.json.impl.RcvrJSON;
+
+/** Describes a property when one is asked to
+ * bind it
+ *
+ * @author Jaroslav Tulach
+ */
+public abstract class PropertyBinding {
+    PropertyBinding() {
+    }
+
+    static {
+        new PropertyBindingAccessor() {
+            @Override
+            protected JSONCall newCall(BrwsrCtx ctx, RcvrJSON callback, String headers, String urlBefore, String urlAfter, String method, Object data) {
+                return new JSONCall(ctx, callback, headers, urlBefore, urlAfter, method, data);
+            }
+
+            @Override
+            protected Bindings bindings(Proto proto, boolean initialize, Object copyFrom) {
+                return initialize ? proto.initBindings(copyFrom) : proto.getBindings();
+            }
+
+            @Override
+            protected void notifyChange(Proto proto, int propIndex) {
+                proto.onChange(propIndex);
+            }
+
+            @Override
+            protected Proto findProto(Proto.Type<?> type, Object object) {
+                return type.protoFor(object);
+            }
+
+            @Override
+            protected <Model> Model cloneTo(Proto.Type<Model> type, Model model, BrwsrCtx c) {
+                return type.cloneTo(model, c);
+            }
+
+            @Override
+            protected Object read(Proto.Type<?> from, BrwsrCtx c, Object data) {
+                return from.read(c, data);
+            }
+
+            @Override
+            protected <M> PropertyBinding newBinding(
+                Proto.Type<M> access, Bindings<?> bindings, String name, int index, M model, byte propertyType) {
+                return new Impl(model, bindings, name, index, access, propertyType);
+            }
+        };
+    }
+
+    /** Name of the property this binding represents.
+     * @return name of the property
+     */
+    public abstract String getPropertyName();
+
+    /** Changes value of the property. Can be called only on dedicated
+     * thread. See {@link Technology#runSafe(java.lang.Runnable)}.
+     *
+     * @param v new value of the property
+     */
+    public abstract void setValue(Object v);
+
+    /** Obtains current value of the property this binding represents.
+     * Can be called only on dedicated
+     * thread. See {@link Technology#runSafe(java.lang.Runnable)}.
+     *
+     * @return the value or <code>null</code>
+     */
+    public abstract Object getValue();
+
+    /** Is this property read only?. Or can one call {@link #setValue(java.lang.Object)}?
+     * The property can still change, but only as a result of other
+     * properties being changed, just like {@link ComputedProperty} can.
+     *
+     * @return true, if this property is read only
+     */
+    public abstract boolean isReadOnly();
+
+    /** Is this property constant?. If a property is constant, than its
+     * value cannot changed after it is read.
+     *
+     * @return true, if this property is constant
+     * @since 1.3
+     */
+    public abstract boolean isConstant();
+
+    /** Returns identical version of the binding, but one that holds on the
+     * original model object via weak reference.
+     *
+     * @return binding that uses weak reference
+     * @since 1.1
+     */
+    public abstract PropertyBinding weak();
+
+    private static abstract class AImpl<M> extends PropertyBinding {
+        public final String name;
+        public final byte propertyType;
+        final Proto.Type<M> access;
+        final Bindings<?> bindings;
+        final int index;
+
+        public AImpl(Bindings<?> bindings, String name, int index, Proto.Type<M> access, byte propertyType) {
+            this.bindings = bindings;
+            this.name = name;
+            this.index = index;
+            this.access = access;
+            this.propertyType = propertyType;
+        }
+
+        protected abstract M model();
+
+        @Override
+        public void setValue(Object v) {
+            M model = model();
+            if (model == null) {
+                return;
+            }
+            access.setValue(model, index, v);
+        }
+
+        @Override
+        public Object getValue() {
+            M model = model();
+            if (model == null) {
+                return null;
+            }
+            Object v = access.getValue(model, index);
+            Object r = JSON.find(v, bindings);
+            return r == null ? v : r;
+        }
+
+        @Override
+        public boolean isReadOnly() {
+            return (propertyType & 1) != 0;
+        }
+
+        @Override
+        public boolean isConstant() {
+            return (propertyType & 2) != 0;
+        }
+
+        @Override
+        public String getPropertyName() {
+            return name;
+        }
+    } // end of PBData
+
+    private static final class Impl<M> extends AImpl<M> {
+        private final M model;
+
+        public Impl(M model, Bindings<?> bindings, String name, int index, Proto.Type<M> access, byte propertyType) {
+            super(bindings, name, index, access, propertyType);
+            this.model = model;
+        }
+
+        @Override
+        protected M model() {
+            return model;
+        }
+
+        @Override
+        public PropertyBinding weak() {
+            return new Weak(model, bindings, name, index, access, propertyType);
+        }
+    }
+
+    private static final class Weak<M> extends AImpl<M> {
+        private final Reference<M> ref;
+        public Weak(M model, Bindings<?> bindings, String name, int index, Proto.Type<M> access, byte propertyType) {
+            super(bindings, name, index, access, propertyType);
+            this.ref = new WeakReference<M>(model);
+        }
+
+        @Override
+        protected M model() {
+            return ref.get();
+        }
+
+        @Override
+        public PropertyBinding weak() {
+            return this;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/main/java/org/netbeans/html/json/spi/Proto.java
----------------------------------------------------------------------
diff --git a/json/src/main/java/org/netbeans/html/json/spi/Proto.java b/json/src/main/java/org/netbeans/html/json/spi/Proto.java
new file mode 100644
index 0000000..73c36e7
--- /dev/null
+++ b/json/src/main/java/org/netbeans/html/json/spi/Proto.java
@@ -0,0 +1,953 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.json.spi;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import net.java.html.BrwsrCtx;
+import net.java.html.json.ComputedProperty;
+import net.java.html.json.Model;
+import net.java.html.json.Property;
+import org.netbeans.html.json.impl.Bindings;
+import org.netbeans.html.json.impl.JSON;
+import org.netbeans.html.json.impl.JSON.WS;
+import org.netbeans.html.json.impl.JSONList;
+import org.netbeans.html.json.impl.PropertyBindingAccessor;
+import org.netbeans.html.json.impl.RcvrJSON;
+import org.netbeans.html.json.impl.RcvrJSON.MsgEvnt;
+
+/** Object associated with one instance of a model generated by the
+ * {@link Model} annotation. Contains methods the generated class can
+ * use to communicate with behind the scene associated {@link Technology}.
+ * Each {@link Proto} object is associated with <a href="http://wiki.apidesign.org/wiki/Singletonizer">
+ * singletonizer</a>-like interface {@link Type} which provides the
+ * associated {@link Technology} the necessary information about the
+ * generated {@link Model} class.
+ *
+ * @author Jaroslav Tulach
+ * @since 0.7
+ */
+public final class Proto {
+    private final Object obj;
+    private final Type type;
+    private final net.java.html.BrwsrCtx context;
+    private org.netbeans.html.json.impl.Bindings ko;
+    private Observers observers;
+
+    Proto(Object obj, Type type, BrwsrCtx context) {
+        this.obj = obj;
+        this.type = type;
+        this.context = context;
+    }
+
+    /** Browser context this proto object and its associated model
+     * are operating-in.
+     *
+     * @return the associated context
+     */
+    public BrwsrCtx getContext() {
+        return context;
+    }
+
+    /** Acquires global lock to compute a {@link ComputedProperty derived property}
+     * on this proto object. This proto object must not be locked yet. No
+     * dependency tracking is performed.
+     *
+     * @throws IllegalStateException if already locked
+     */
+    public void acquireLock() throws IllegalStateException {
+        acquireLock(null);
+    }
+
+    /** Acquires global lock to compute a {@link ComputedProperty derived property}
+     * on this proto object. This proto object must not be locked yet. The
+     * name of the property is used to track dependencies on own
+     * properties of other proto objects - when they are changed, this
+     * {@link #valueHasMutated(java.lang.String) property is changed too}.
+     *
+     * @param propName name of property we are about to compute
+     * @throws IllegalStateException thrown when there is a cyclic
+     *   call is detected
+     * @since 0.9
+     */
+    public void acquireLock(String propName) throws IllegalStateException {
+        Observers.beginComputing(this, propName);
+    }
+
+    /** A property on this proto object is about to be accessed. Verifies
+     * whether this proto object is accessible - e.g. it has not been
+     * {@link #acquireLock() locked yet}. If everything is OK, the
+     * <code>propName</code> is recorded in the chain of dependencies
+     * tracked by {@link #acquireLock(java.lang.String)} and watched by
+     * {@link #valueHasMutated(java.lang.String)}.
+     *
+     * @param propName name of the property that is requested
+     * @throws IllegalStateException if the model is locked
+     * @since 0.9
+     */
+    public void accessProperty(String propName) throws IllegalStateException {
+        Observers.accessingValue(this, propName);
+    }
+
+    /** Verifies the model is not locked otherwise throws an exception.
+     * @throws IllegalStateException if the model is locked
+     */
+    public void verifyUnlocked() throws IllegalStateException {
+        Observers.verifyUnlocked(this);
+    }
+
+    /** When modifications are over, the model is switched into
+     * unlocked state by calling this method.
+     */
+    public void releaseLock() {
+        Observers.finishComputing(this);
+    }
+
+    /** Whenever model changes a property. It should notify the
+     * associated technology by calling this method.
+     * Since 0.8.3: This method may be called by any thread - it reschedules
+     * its actual execution into appropriate one by using
+     * {@link BrwsrCtx#execute(java.lang.Runnable)}.
+     *
+     * @param propName name of the changed property
+     */
+    public void valueHasMutated(final String propName) {
+        context.execute(new Runnable() {
+            @Override
+            public void run() {
+                if (ko != null) {
+                    ko.valueHasMutated(propName, null, null);
+                }
+                Observers.valueHasMutated(Proto.this, propName);
+            }
+        });
+    }
+
+    /** Whenever model changes a propertyit should notify the
+     * associated technology. Either by calling this method
+     * (if the new value is known and different to the old one) or
+     * via (slightly ineffective) {@link #valueHasMutated(java.lang.String)}
+     * method.
+     * Since 0.8.3: This method may be called by any thread - it reschedules
+     * its actual execution into appropriate one by using
+     * {@link BrwsrCtx#execute(java.lang.Runnable)}.
+     *
+     * @param propName name of the changed property
+     * @param oldValue provides previous value of the property
+     * @param newValue provides new value of the property
+     * @since 0.7.6
+     */
+    public void valueHasMutated(
+        final String propName, final Object oldValue, final Object newValue
+    ) {
+        context.execute(new Runnable() {
+            @Override
+            public void run() {
+                if (ko != null) {
+                    ko.valueHasMutated(propName, oldValue, newValue);
+                }
+                Observers.valueHasMutated(Proto.this, propName);
+            }
+        });
+    }
+
+    /** Initializes the associated model in the current {@link #getContext() context}.
+     * In case of <em>knockout.js</em> technology, applies given bindings
+     * of the current model to the <em>body</em> element of the page.
+     */
+    public void applyBindings() {
+        initBindings(null).applyBindings(null);
+    }
+
+    /** Initializes the associated model to the specified element's subtree.
+     * The technology is taken from the current {@link #getContext() context} and
+     * in case of <em>knockout.js</em> applies given bindings
+     * of the current model to the element of the page with 'id' attribute
+     * set to the specified <code>id</code> value.
+     *
+     * @param id the id of element to apply the binding to
+     * @since 1.1
+     * @see Technology.ApplyId
+     */
+    public void applyBindings(String id) {
+        initBindings(null).applyBindings(id);
+    }
+
+    /** Invokes the provided runnable in the {@link #getContext() context}
+     * of the browser. If the caller is already on the right thread, the
+     * <code>run.run()</code> is invoked immediately and synchronously.
+     * Otherwise the method returns immediately and the <code>run()</code>
+     * method is performed later
+     *
+     * @param run the action to execute
+     */
+    public void runInBrowser(Runnable run) {
+        context.execute(run);
+    }
+
+    /** Invokes the specified function index in the {@link #getContext() context}
+     * of the browser. If the caller is already on the right thread, the
+     * index-th function is invoked immediately and synchronously.
+     * Otherwise the method returns immediately and the function is invoked
+     * later.
+     *
+     * @param index the index of the function as will be passed to
+     *   {@link Type#call(java.lang.Object, int, java.lang.Object, java.lang.Object)}
+     *   method
+     * @param args array of arguments that will be passed as
+     *   <code>data</code> argument of the <code>call</code> method.
+     * @since 0.7.6
+     */
+    public void runInBrowser(final int index, final Object... args) {
+        context.execute(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    type.call(obj, index, args, null);
+                } catch (Exception ex) {
+                    ex.printStackTrace();
+                }
+            }
+        });
+    }
+
+    /** Initializes the provided collection with a content of the <code>array</code>.
+     * The initialization can only be done soon after the the collection
+     * is created, otherwise an exception is throw
+     *
+     * @param to the collection to initialize (assumed to be empty)
+     * @param array the array to add to the collection
+     * @throws IllegalStateException if the system has already been initialized
+     */
+    public void initTo(Collection<?> to, Object array) {
+        if (ko != null) {
+            throw new IllegalStateException();
+        }
+        if (to instanceof JSONList) {
+           ((JSONList)to).init(array);
+        } else {
+            JSONList.init(to, array);
+        }
+    }
+
+    /** Takes an object representing JSON result and extract some of its
+     * properties. It is assumed that the <code>props</code> and
+     * <code>values</code> arrays have the same length.
+     *
+     * @param json the JSON object (actual type depends on the associated
+     *   {@link Technology})
+     * @param props list of properties to extract
+     * @param values array that will be filled with extracted values
+     */
+    public void extract(Object json, String[] props, Object[] values) {
+        JSON.extract(context, json, props, values);
+    }
+
+    /** Converts raw JSON <code>data</code> into a Java {@link Model} class.
+     *
+     * @param <T> type of the model class
+     * @param modelClass the type of the class to create
+     * @param data the raw JSON data
+     * @return newly created instance of the model class
+     */
+    public <T> T read(Class<T> modelClass, Object data) {
+        return JSON.read(context, modelClass, data);
+    }
+
+    /** Initializes asynchronous JSON connection to specified URL. Delegates
+     * to {@link #loadJSON(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object, java.lang.Object...) }
+     * with no extra parameters.
+     *
+     * @param index the callback index to be used when a reply is received
+     *   to call {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object)}.
+     *
+     * @param urlBefore the part of the URL before JSON-P callback parameter
+     * @param urlAfter the rest of the URL or <code>null</code> if no JSON-P is used
+     * @param method method to use for connection to the server
+     * @param data string, number or a {@link Model} generated class to send to
+     *    the server when doing a query
+     */
+    public void loadJSON(final int index,
+        String urlBefore, String urlAfter, String method,
+        final Object data
+    ) {
+        loadJSON(index, urlBefore, urlAfter, method, data, new Object[0]);
+    }
+
+    /** Initializes asynchronous JSON connection to specified URL. The
+     * method returns immediately and later does callback later.
+     *
+     * @param index the callback index to be used when a reply is received
+     *   to call {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object)}.
+     *
+     * @param urlBefore the part of the URL before JSON-P callback parameter
+     * @param urlAfter the rest of the URL or <code>null</code> if no JSON-P is used
+     * @param method method to use for connection to the server
+     * @param data string, number or a {@link Model} generated class to send to
+     *    the server when doing a query
+     * @param params extra params to pass back when calling
+     *   {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object, java.lang.Object[])}
+     * @since 0.8.1
+     */
+    public void loadJSON(final int index,
+        String urlBefore, String urlAfter, String method,
+        final Object data, final Object... params
+    ) {
+        loadJSONWithHeaders(index, null, urlBefore, urlAfter, method, data, params);
+    }
+
+    /** Initializes asynchronous JSON connection to specified URL. The
+     * method returns immediately and later does callback later.
+     *
+     * @param index the callback index to be used when a reply is received
+     *   to call {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object)}.
+     *
+     * @param headers headers to use for the request or <code>null</code> to use default ones
+     * @param urlBefore the part of the URL before JSON-P callback parameter
+     * @param urlAfter the rest of the URL or <code>null</code> if no JSON-P is used
+     * @param method method to use for connection to the server
+     * @param data string, number or a {@link Model} generated class to send to
+     *    the server when doing a query
+     * @param params extra params to pass back when calling
+     *   {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object, java.lang.Object[])}
+     * @since 1.2
+     */
+    public void loadJSONWithHeaders(final int index,
+        String headers,
+        String urlBefore, String urlAfter, String method,
+        final Object data, final Object... params
+    ) {
+        class Rcvr extends RcvrJSON {
+            @Override
+            protected void onMessage(MsgEvnt msg) {
+                type.onMessage(obj, index, 1, msg.getValues(), params);
+            }
+
+            @Override
+            protected void onError(MsgEvnt msg) {
+                type.onMessage(obj, index, 2, msg.getException(), params);
+            }
+        }
+        JSONCall call = PropertyBindingAccessor.createCall(
+            context, new Rcvr(), headers, urlBefore, urlAfter, method, data
+        );
+        Transfer t = JSON.findTransfer(context);
+        t.loadJSON(call);
+    }
+
+    /** Opens new WebSocket connection to the specified URL.
+     *
+     * @param index the index to use later during callbacks to
+     *   {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object)}
+     * @param url the <code>ws://</code> or <code>wss://</code> URL to connect to
+     * @param data data to send to server (usually <code>null</code>)
+     * @return returns a non-null object representing the socket
+     *   which can be used when calling {@link #wsSend(java.lang.Object, java.lang.String, java.lang.Object) }
+     */
+    public Object wsOpen(final int index, String url, Object data) {
+        class WSrcvr extends RcvrJSON {
+            @Override
+            protected void onError(MsgEvnt msg) {
+                type.onMessage(obj, index, 2, msg.getException());
+            }
+
+            @Override
+            protected void onMessage(MsgEvnt msg) {
+                type.onMessage(obj, index, 1, msg.getValues());
+            }
+
+            @Override
+            protected void onClose(MsgEvnt msg) {
+                type.onMessage(obj, index, 3, null);
+            }
+
+            @Override
+            protected void onOpen(MsgEvnt msg) {
+                type.onMessage(obj, index, 0, null);
+            }
+        }
+        WS ws = WS.create(JSON.findWSTransfer(context), new WSrcvr());
+        ws.send(context, null, url, data);
+        return ws;
+    }
+
+    /** Sends a message to existing socket.
+     *
+     * @param webSocket the socket to send message to
+     * @param url the <code>ws://</code> or <code>wss://</code> URL to connect to,
+     *    preferably the same as the one used when the socket was
+     *    {@link #wsOpen(int, java.lang.String, java.lang.Object) opened}
+     * @param data the data to send or <code>null</code> if the socket is
+     *    supposed to be closed
+     */
+    public void wsSend(Object webSocket, String url, Object data) {
+        ((JSON.WS)webSocket).send(context, null, url, data);
+    }
+
+    /** Converts raw data (one of its properties) to string representation.
+     *
+     * @param data the object
+     * @param propName the name of object property or <code>null</code>
+     *   if the whole object should be converted
+     * @return the string representation of the object or its property
+     */
+    public String toString(Object data, String propName) {
+        return JSON.toString(context, data, propName);
+    }
+
+    /** Converts raw data (one of its properties) to a number representation.
+     *
+     * @param data the object
+     * @param propName the name of object property or <code>null</code>
+     *   if the whole object should be converted
+     * @return the number representation of the object or its property
+     */
+    public Number toNumber(Object data, String propName) {
+        return JSON.toNumber(context, data, propName);
+    }
+
+    /** Converts raw JSON data into a {@link Model} class representation.
+     *
+     * @param <T> type of the model to create
+     * @param type class of the model to create
+     * @param data raw JSON data (depends on associated {@link Technology})
+     * @return new instances of the model class filled with values from the
+     *   <code>data</code> object
+     */
+    public <T> T toModel(Class<T> type, Object data) {
+        return JSON.toModel(context, type, data, null);
+    }
+
+    /** Creates new JSON like observable list.
+     *
+     * @param <T> the type of the list elements
+     * @param propName name of a property this list is associated with
+     * @param onChange index of the property to use when the list is modified
+     *   during callback to {@link Type#onChange(java.lang.Object, int)}.
+     *   If the value is {@link Integer#MIN_VALUE}, then the list is
+     *   not fully {@link Property#mutable()} and throws {@link UnsupportedOperationException}
+     *   on such attempts.
+     * @param dependingProps the array of {@link ComputedProperty derived properties}
+     *   that depend on the value of the list
+     * @return new, empty list associated with this proto-object and its model
+     */
+    public <T> List<T> createList(String propName, int onChange, String... dependingProps) {
+        return new JSONList<T>(this, propName, onChange, dependingProps);
+    }
+
+    /** Copies content of one collection to another, re-assigning all its
+     * elements from their current context to the new <code>ctx</code>.
+     *
+     * @param <T> type of the collections
+     * @param to the target collection to be filled with cloned values
+     * @param ctx context for the new collection
+     * @param from original collection with its data
+     */
+    public <T> void cloneList(Collection<T> to, BrwsrCtx ctx, Collection<T> from) {
+        Boolean isModel = null;
+        for (T t : from) {
+            if (isModel == null) {
+                isModel = JSON.isModel(t.getClass());
+            }
+            if (isModel) {
+                to.add(JSON.bindTo(t, ctx));
+            } else {
+                to.add(t);
+            }
+        }
+    }
+
+    //
+    // internal state
+    //
+
+    final String toStr() {
+        return "Proto[" + obj + "]@" + Integer.toHexString(System.identityHashCode(this));
+    }
+
+    final Bindings initBindings(Object originalObject) {
+        if (ko == null) {
+            Bindings b = Bindings.apply(context);
+            PropertyBinding[] pb = new PropertyBinding[type.propertyNames.length];
+            for (int i = 0; i < pb.length; i++) {
+                pb[i] = b.registerProperty(
+                    type.propertyNames[i], i, obj, type, type.propertyType[i]
+                );
+            }
+            FunctionBinding[] fb = new FunctionBinding[type.functions.length];
+            for (int i = 0; i < fb.length; i++) {
+                fb[i] = FunctionBinding.registerFunction(
+                    type.functions[i], i, obj, type
+                );
+            }
+            ko = b;
+            b.finish(obj, originalObject, pb, fb);
+        }
+        return ko;
+    }
+
+    final Bindings getBindings() {
+        return ko;
+    }
+
+    final void onChange(int index) {
+        type.onChange(obj, index);
+    }
+
+    final Observers observers(boolean create) {
+        if (create && observers == null) {
+            observers = new Observers();
+        }
+        return observers;
+    }
+
+    /** Functionality used by the code generated by annotation
+     * processor for the {@link net.java.html.json.Model} annotation.
+     *
+     * @param <Model> the generated class
+     * @since 0.7
+     */
+    public static abstract class Type<Model> {
+        private final Class<Model> clazz;
+        private final String[] propertyNames;
+        private final byte[] propertyType;
+        private final String[] functions;
+
+        /** Constructor for subclasses generated by the annotation processor
+         * associated with {@link net.java.html.json.Model} annotation.
+         *
+         * @param clazz the generated model class
+         * @param modelFor the original class annotated by the {@link net.java.html.json.Model} annotation.
+         * @param properties number of properties the class has
+         * @param functions  number of functions the class has
+         */
+        protected Type(
+            Class<Model> clazz, Class<?> modelFor, int properties, int functions
+        ) {
+            assert getClass().getName().endsWith("$Html4JavaType");
+            try {
+                assert getClass().getDeclaringClass() == clazz;
+            } catch (SecurityException ex) {
+                // OK, no check
+            }
+            this.clazz = clazz;
+            this.propertyNames = new String[properties];
+            this.propertyType = new byte[properties];
+            this.functions = new String[functions];
+            JSON.register(clazz, this);
+        }
+
+        /** Registers property for the type. It is expected each index
+         * is initialized only once.
+         *
+         * @param name name of the property
+         * @param index index of the property
+         * @param readOnly is the property read only?
+         */
+        protected final void registerProperty(String name, int index, boolean readOnly) {
+            assert propertyNames[index] == null;
+            propertyNames[index] = name;
+            propertyType[index] = (byte) (readOnly ? 1 : 0);
+        }
+
+        /** Registers property for the type. It is expected each index
+         * is initialized only once. The difference between <code>readOnly</code>
+         * and <code>constant</code> is: The <code>constant</code> value is
+         * assigned only at the beginning and never changed then - like the
+         * {@link Property#mutable() non-mutable} property. On the other
+         * hand, a <code>readOnly</code> property can change its value,
+         * but not via a setter - just like {@link ComputedProperty}.
+         *
+         * @param name name of the property
+         * @param index index of the property
+         * @param readOnly is the property read only?
+         * @param constant is the property assigned once and never changed again?
+         * @since 1.3
+         */
+        protected final void registerProperty(
+            String name, int index, boolean readOnly, boolean constant
+        ) {
+            assert propertyNames[index] == null;
+            propertyNames[index] = name;
+            propertyType[index] = (byte) ((readOnly ? 1 : 0) | (constant ? 2 : 0));
+        }
+
+        /** Registers function of given name at given index.
+         *
+         * @param name name of the function
+         * @param index name of the type
+         */
+        protected final void registerFunction(String name, int index) {
+            assert functions[index] == null;
+            functions[index] = name;
+        }
+
+        /** Creates new proto-object for given {@link Model} class bound to
+         * provided context.
+         *
+         * @param obj instance of appropriate {@link Model} class
+         * @param context the browser context
+         * @return new proto-object that the generated class can use for
+         *   communication with the infrastructure
+         */
+        public Proto createProto(Object obj, BrwsrCtx context) {
+            return new Proto(obj, this, context);
+        }
+
+        //
+        // Implemented by subclasses
+        //
+
+        /** Sets value of a {@link #registerProperty(java.lang.String, int, boolean) registered property}
+         * to new value.
+         *
+         * @param model the instance of {@link Model model class}
+         * @param index index of the property used during registration
+         * @param value the value to set the property to
+         */
+        protected abstract void setValue(Model model, int index, Object value);
+
+        /** Obtains and returns value of a
+         * {@link #registerProperty(java.lang.String, int, boolean) registered property}.
+         *
+         * @param model the instance of {@link Model model class}
+         * @param index index of the property used during registration
+         * @return current value of the property
+         */
+        protected abstract Object getValue(Model model, int index);
+
+        /** Invokes a {@link #registerFunction(java.lang.String, int)} registered function
+         * on given object.
+         *
+         * @param model the instance of {@link Model model class}
+         * @param index index of the property used during registration
+         * @param data the currently selected object the function is about to operate on
+         * @param event the event that triggered the event
+         * @throws Exception the method can throw exception which is then logged
+         */
+        protected abstract void call(Model model, int index, Object data, Object event)
+        throws Exception;
+
+        /** Re-binds the model object to new browser context.
+         *
+         * @param model the instance of {@link Model model class}
+         * @param ctx browser context to clone the object to
+         * @return new instance of the model suitable for new context
+         */
+        protected abstract Model cloneTo(Model model, BrwsrCtx ctx);
+
+        /** Reads raw JSON data and converts them to our model class.
+         *
+         * @param c the browser context to work in
+         * @param json raw JSON data to get values from
+         * @return new instance of model class filled by the data
+         */
+        protected abstract Model read(BrwsrCtx c, Object json);
+
+        /** Called when a {@link #registerProperty(java.lang.String, int, boolean) registered property}
+         * changes its value.
+         *
+         * @param model the object that has the property
+         * @param index the index of the property during registration
+         */
+        protected abstract void onChange(Model model, int index);
+
+        /** Finds out if there is an associated proto-object for given
+         * object.
+         *
+         * @param object an object, presumably (but not necessarily) instance of Model class
+         * @return associated proto-object or <code>null</code>
+         */
+        protected abstract Proto protoFor(Object object);
+
+        /** Called to report results of asynchronous over-the-wire
+         * communication. Result of calling {@link Proto#wsOpen(int, java.lang.String, java.lang.Object)}
+         * or {@link Proto#loadJSON(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object, java.lang.Object...)}.
+         *
+         * @param model the instance of the model class
+         * @param index index used during initiating the communication (via <code>loadJSON</code> or <code>wsOpen</code> calls)
+         * @param type type of the message: 0 - onOpen, 1 - onMessage, 2 - onError, 3 - onClose -
+         *   not all messages are applicable to all communication protocols (JSON has only 1 and 2).
+         * @param data <code>null</code> or string, number or a {@link Model} class
+         *   obtained to the server as a response
+         */
+        protected void onMessage(Model model, int index, int type, Object data) {
+            onMessage(model, index, type, data, new Object[0]);
+        }
+
+        /** Called to report results of asynchronous over-the-wire
+         * communication. Result of calling {@link Proto#wsOpen(int, java.lang.String, java.lang.Object)}
+         * or {@link Proto#loadJSON(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object, java.lang.Object...)}.
+         *
+         * @param model the instance of the model class
+         * @param index index used during initiating the communication (via <code>loadJSON</code> or <code>wsOpen</code> calls)
+         * @param type type of the message: 0 - onOpen, 1 - onMessage, 2 - onError, 3 - onClose -
+         *   not all messages are applicable to all communication protocols (JSON has only 1 and 2).
+         * @param data <code>null</code> or string, number or a {@link Model} class
+         *   obtained to the server as a response
+         * @param params extra parameters as passed for example to
+         *   {@link Proto#loadJSON(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object, java.lang.Object...)}
+         *   method
+         * @since 0.8.1
+         */
+        protected void onMessage(Model model, int index, int type, Object data, Object[] params) {
+            onMessage(model, index, type, data);
+        }
+
+        //
+        // Various support methods the generated classes use
+        //
+
+        /** Converts and array of raw JSON objects into an array of typed
+         * Java {@link Model} classes.
+         *
+         * @param <T> the type of the destination array
+         * @param context browser context to use
+         * @param src array of raw JSON objects
+         * @param destType type of the individual array elements
+         * @param dest array to be filled with read type instances
+         */
+        public <T> void copyJSON(BrwsrCtx context, Object[] src, Class<T> destType, T[] dest) {
+            for (int i = 0; i < src.length && i < dest.length; i++) {
+                dest[i] = org.netbeans.html.json.impl.JSON.read(context, destType, src[i]);
+            }
+        }
+
+        /** Compares two objects that can be converted to integers.
+         * @param a first value
+         * @param b second value
+         * @return true if they are the same
+         */
+        public final boolean isSame(int a, int b) {
+            return a == b;
+        }
+
+        /** Compares two objects that can be converted to (floating point)
+         * numbers.
+         * @param a first value
+         * @param b second value
+         * @return  true if they are the same
+         */
+        public final boolean isSame(double a, double b) {
+            return a == b;
+        }
+
+        /** Compares two objects for being the same - e.g. either <code>==</code>
+         * or <code>equals</code>.
+         * @param a first value
+         * @param b second value
+         * @return true if they are equals
+         */
+        public final boolean isSame(Object a, Object b) {
+            if (a == b) {
+                return true;
+            }
+            if (a == null || b == null) {
+                return false;
+            }
+            return a.equals(b);
+        }
+
+        /** Cumulative hash function. Adds hashcode of the object to the
+         * previous value.
+         * @param o the object (or <code>null</code>)
+         * @param h the previous value of the hash
+         * @return new hash - the old one xor the object's one
+         */
+        public final int hashPlus(Object o, int h) {
+            return o == null ? h : h ^ o.hashCode();
+        }
+
+        /** Converts an object to its JSON value.
+         *
+         * @param obj the object to convert
+         * @return JSON representation of the object
+         */
+        public final String toJSON(Object obj) {
+            return JSON.toJSON(obj);
+        }
+
+        /** Converts the value to string.
+         *
+         * @param val the value
+         * @return the converted value
+         */
+        public final String stringValue(Object val) {
+            return JSON.stringValue(val);
+        }
+
+        /** Converts the value to number.
+         *
+         * @param val the value
+         * @return the converted value
+         */
+        public final Number numberValue(Object val) {
+            return JSON.numberValue(val);
+        }
+
+        /** Converts the value to character.
+         *
+         * @param val the value
+         * @return the converted value
+         */
+        public final Character charValue(Object val) {
+            return JSON.charValue(val);
+        }
+
+        /** Converts the value to boolean.
+         *
+         * @param val the value
+         * @return the converted value
+         */
+        public final Boolean boolValue(Object val) {
+            return JSON.boolValue(val);
+        }
+
+        /** Extracts value of specific type from given object.
+         *
+         * @param <T> the type of object one is interested in
+         * @param type the type
+         * @param val the object to convert to type
+         * @return the converted value
+         */
+        public final <T> T extractValue(Class<T> type, Object val) {
+            if (Number.class.isAssignableFrom(type)) {
+                val = numberValue(val);
+            }
+            if (Boolean.class == type) {
+                val = boolValue(val);
+            }
+            if (String.class == type) {
+                val = stringValue(val);
+            }
+            if (Character.class == type) {
+                val = charValue(val);
+            }
+            if (Integer.class == type) {
+                val = val instanceof Number ? ((Number) val).intValue() : 0;
+            }
+            if (Long.class == type) {
+                val = val instanceof Number ? ((Number) val).longValue() : 0;
+            }
+            if (Short.class == type) {
+                val = val instanceof Number ? ((Number) val).shortValue() : 0;
+            }
+            if (Byte.class == type) {
+                val = val instanceof Number ? ((Number) val).byteValue() : 0;
+            }
+            if (Double.class == type) {
+                val = val instanceof Number ? ((Number) val).doubleValue() : Double.NaN;
+            }
+            if (Float.class == type) {
+                val = val instanceof Number ? ((Number) val).floatValue() : Float.NaN;
+            }
+            if (type.isEnum() && val instanceof String) {
+                val = Enum.valueOf(type.asSubclass(Enum.class), (String)val);
+            }
+            return type.cast(val);
+        }
+
+        /** Special dealing with array &amp; {@link List} values. This method
+         * takes the provided collection, empties it and fills it again
+         * with values extracted from <code>value</code> (which is supposed
+         * to be an array).
+         *
+         * @param <T> the type of list elements
+         * @param arr collection to fill with elements in value
+         * @param type the type of elements in the collection
+         * @param value array of elements to put into the collecition. If
+         *   value is not an array it is wrapped into array with only element
+         * @since 1.0
+         */
+        public final <T> void replaceValue(Collection<? super T> arr, Class<T> type, Object value) {
+            List<T> tmp = new ArrayList<T>();
+            if (value instanceof Object[]) {
+                for (Object e : (Object[]) value) {
+                    tmp.add(extractValue(type, e));
+                }
+            } else if (value instanceof byte[]) {
+                for (Object e : (byte[]) value) {
+                    tmp.add(extractValue(type, e));
+                }
+            } else if (value instanceof short[]) {
+                for (Object e : (short[]) value) {
+                    tmp.add(extractValue(type, e));
+                }
+            } else if (value instanceof int[]) {
+                for (Object e : (int[]) value) {
+                    tmp.add(extractValue(type, e));
+                }
+            } else if (value instanceof char[]) {
+                for (Object e : (char[]) value) {
+                    tmp.add(extractValue(type, e));
+                }
+            } else if (value instanceof long[]) {
+                for (Object e : (long[]) value) {
+                    tmp.add(extractValue(type, e));
+                }
+            } else if (value instanceof float[]) {
+                for (Object e : (float[]) value) {
+                    tmp.add(extractValue(type, e));
+                }
+            } else if (value instanceof double[]) {
+                for (Object e : (double[]) value) {
+                    tmp.add(extractValue(type, e));
+                }
+            } else if (value instanceof boolean[]) {
+                for (Object e : (boolean[]) value) {
+                    tmp.add(extractValue(type, e));
+                }
+            } else {
+                tmp.add(extractValue(type, value));
+            }
+            if (arr instanceof JSONList) {
+                JSONList jsList = (JSONList) arr;
+                jsList.fastReplace(tmp);
+            } else {
+                arr.clear();
+                arr.addAll(tmp);
+            }
+        }
+    }
+}


[17/24] incubator-netbeans-html4j git commit: [INFRA-15006] Initial donation of HTML/Java NetBeans API. Equivalent to the content of html4j-donation-review.zip donated as part of ApacheNetBeansDonation1.zip with SHA256 being 7f2ca0f61953a190613c9a0fbcc1b

Posted by jt...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/test/java/org/netbeans/html/boot/impl/JsMethods.java
----------------------------------------------------------------------
diff --git a/boot/src/test/java/org/netbeans/html/boot/impl/JsMethods.java b/boot/src/test/java/org/netbeans/html/boot/impl/JsMethods.java
new file mode 100644
index 0000000..e680bdf
--- /dev/null
+++ b/boot/src/test/java/org/netbeans/html/boot/impl/JsMethods.java
@@ -0,0 +1,157 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.impl;
+
+import java.util.Map;
+import net.java.html.js.JavaScriptBody;
+import net.java.html.js.JavaScriptResource;
+
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+@JavaScriptResource("jsmethods.js")
+public class JsMethods {
+    private java.lang.Object value;
+    
+    @JavaScriptBody(args = {}, body = "return 42;")
+    public static java.lang.Object fortyTwo() {
+        return -42;
+    }
+    
+    @JavaScriptBody(args = {"x", "y" }, body = "return x + y;")
+    public static native int plus(int x, int y);
+    
+    @JavaScriptBody(args = {"x"}, body = "return x;")
+    public static native int plus(int x);
+    
+    @JavaScriptBody(args = {}, body = "return this;")
+    public static native java.lang.Object staticThis();
+    
+    @JavaScriptBody(args = {}, body = "return this;")
+    public native java.lang.Object getThis();
+    @JavaScriptBody(args = {"x"}, body = "return x;")
+    public native int plusInst(int x);
+    
+    @JavaScriptBody(args = {}, body = "return true;")
+    public static boolean truth() {
+        return false;
+    }
+    
+    @JavaScriptBody(args = { "r" }, javacall=true, body = "r.@java.lang.Runnable::run()();")
+    public static native void callback(Runnable r);
+    
+    @JavaScriptBody(args = { "at", "arr" }, javacall = true, body =
+          "var a = 0;\n"
+        + "for (var i = 0; i < arr.length; i++) {\n"
+        + "  a = at.@org.netbeans.html.boot.impl.Arithm::sumTwo(II)(a, arr[i]);\n"
+        + "}\n"
+        + "return a;"
+    )
+    private static native int sumArr(Arithm at, int... arr);
+    
+    public static int sumArr(int... arr) {
+        return sumArr(new Arithm(), arr);
+    }
+    
+    @JavaScriptBody(args = { "x", "y" }, body = "return mul(x, y);")
+    public static native int useExternalMul(int x, int y);
+    
+    @JavaScriptBody(args = { "m" }, javacall = true, body = "return m.@org.netbeans.html.boot.impl.JsMethods::getThis()();")
+    public static native JsMethods returnYourSelf(JsMethods m);
+    
+    @JavaScriptBody(args = { "x", "y" }, javacall = true, body = "return @org.netbeans.html.boot.impl.JsMethods::useExternalMul(II)(x, y);")
+    public static native int staticCallback(int x, int y);
+
+    @JavaScriptBody(args = { "v" }, javacall = true, body = "return @java.lang.Integer::parseInt(Ljava/lang/String;)(v);")
+    public static native int parseInt(String v);
+    
+    @JavaScriptBody(args = { "v" }, body = "return v.toString();")
+    public static native String fromEnum(Enm v);
+    
+    @JavaScriptBody(args = "arr", body = "return arr;")
+    public static native java.lang.Object[] arr(java.lang.Object[] arr);
+    
+    @JavaScriptBody(args = { "useA", "useB", "a", "b" }, body = "var l = 0;"
+        + "if (useA) l += a;\n"
+        + "if (useB) l += b;\n"
+        + "return l;\n"
+    )
+    public static native long chooseLong(boolean useA, boolean useB, long a, long b);
+    
+    protected void onError(java.lang.Object o) throws Exception {
+        value = o;
+    }
+    
+    java.lang.Object getError() {
+        return value;
+    }
+    
+    @JavaScriptBody(args = { "err" }, javacall = true, body = 
+        "this.@org.netbeans.html.boot.impl.JsMethods::onError(Ljava/lang/Object;)(err);"
+      + "return this.@org.netbeans.html.boot.impl.JsMethods::getError()();"
+    )
+    public native java.lang.Object recordError(java.lang.Object err);
+    
+    @JavaScriptBody(args = { "x", "y" }, body = "return x + y;")
+    public static int plusOrMul(int x, int y) {
+        return x * y;
+    }
+    
+    @JavaScriptBody(args = { "x" }, keepAlive = false, body = "throw 'Do not call me!'")
+    public static native int checkAllowGC(java.lang.Object x);
+
+    @JavaScriptBody(args = { "map", "value" }, javacall = true, body =
+       "map.@java.util.Map::put(Ljava/lang/Object;Ljava/lang/Object;)('key',value);"
+    )
+    public static native void callParamTypes(Map<String,Integer> map, int value);
+
+    @JavaScriptBody(args = { "a", "b" }, body = "return [ a, b ];")
+    public static native double[] both(double a, double b);
+    
+    enum Enm {
+        A, B;
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/test/java/org/netbeans/html/boot/impl/KeepAliveTest.java
----------------------------------------------------------------------
diff --git a/boot/src/test/java/org/netbeans/html/boot/impl/KeepAliveTest.java b/boot/src/test/java/org/netbeans/html/boot/impl/KeepAliveTest.java
new file mode 100644
index 0000000..0f8761d
--- /dev/null
+++ b/boot/src/test/java/org/netbeans/html/boot/impl/KeepAliveTest.java
@@ -0,0 +1,113 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.impl;
+
+import java.io.Closeable;
+import java.io.Reader;
+import java.net.URL;
+import java.util.Collection;
+import org.netbeans.html.boot.spi.Fn;
+import static org.testng.Assert.assertEquals;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class KeepAliveTest implements Fn.Presenter, Fn.KeepAlive, FindResources {
+    private Class<?> jsMethods;
+    @Test public void keepAliveIsSetToFalse() throws Exception {
+        Closeable c = Fn.activate(this);
+        Number ret = (Number)jsMethods.getMethod("checkAllowGC", java.lang.Object.class).invoke(null, this);
+        c.close();
+        assertEquals(ret.intValue(), 0, "keepAlive is set to false");
+    }    
+
+    @Test public void keepAliveIsTheDefault() throws Exception {
+        Closeable c = Fn.activate(this);
+        Number ret = (Number)jsMethods.getMethod("plus", int.class, int.class).invoke(null, 40, 2);
+        c.close();
+        assertEquals(ret.intValue(), 1, "keepAlive returns true when the presenter is invoked");
+    }    
+
+    @BeforeMethod
+    public void initClass() throws ClassNotFoundException {
+        ClassLoader loader = FnUtils.newLoader(this, this, KeepAliveTest.class.getClassLoader().getParent());
+        jsMethods = loader.loadClass(JsMethods.class.getName());
+    }
+
+    @Override
+    public Fn defineFn(String code, String[] names, final boolean[] keepAlive) {
+        return new Fn(this) {
+            @Override
+            public java.lang.Object invoke(java.lang.Object thiz, java.lang.Object... args) throws Exception {
+                boolean res = true;
+                if (keepAlive != null) {
+                    for (int i = 0; i < keepAlive.length; i++) {
+                        res &= keepAlive[i];
+                    }
+                }
+                return res ? 1 : 0;
+            }
+        };
+    }
+    
+    @Override
+    public Fn defineFn(String code, String... names) {
+        throw new UnsupportedOperationException("Never called!");
+    }
+
+    @Override
+    public void displayPage(URL page, Runnable onPageLoad) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void loadScript(Reader code) throws Exception {
+    }
+
+    @Override
+    public void findResources(String path, Collection<? super URL> results, boolean oneIsEnough) {
+        URL u = ClassLoader.getSystemClassLoader().getResource(path);
+        if (u != null) {
+            results.add(u);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/test/java/org/netbeans/html/boot/impl/Object.java
----------------------------------------------------------------------
diff --git a/boot/src/test/java/org/netbeans/html/boot/impl/Object.java b/boot/src/test/java/org/netbeans/html/boot/impl/Object.java
new file mode 100644
index 0000000..8c23265
--- /dev/null
+++ b/boot/src/test/java/org/netbeans/html/boot/impl/Object.java
@@ -0,0 +1,49 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.impl;
+
+/** Fake class to cause confusion in the generated code and force it
+ * to use fully qualified names
+ */
+final class Object {
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/test/resources/org/netbeans/html/boot/impl/empty.js
----------------------------------------------------------------------
diff --git a/boot/src/test/resources/org/netbeans/html/boot/impl/empty.js b/boot/src/test/resources/org/netbeans/html/boot/impl/empty.js
new file mode 100644
index 0000000..2ecbb58
--- /dev/null
+++ b/boot/src/test/resources/org/netbeans/html/boot/impl/empty.js
@@ -0,0 +1,42 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/test/resources/org/netbeans/html/boot/impl/jsmethods.js
----------------------------------------------------------------------
diff --git a/boot/src/test/resources/org/netbeans/html/boot/impl/jsmethods.js b/boot/src/test/resources/org/netbeans/html/boot/impl/jsmethods.js
new file mode 100644
index 0000000..f88184b
--- /dev/null
+++ b/boot/src/test/resources/org/netbeans/html/boot/impl/jsmethods.js
@@ -0,0 +1,43 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+function mul(x, y) { return x * y; }

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/context/pom.xml
----------------------------------------------------------------------
diff --git a/context/pom.xml b/context/pom.xml
new file mode 100644
index 0000000..c127c48
--- /dev/null
+++ b/context/pom.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2016 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<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">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.netbeans.html</groupId>
+    <artifactId>pom</artifactId>
+    <version>2.0-SNAPSHOT</version>
+  </parent>
+  <groupId>org.netbeans.html</groupId>
+  <artifactId>net.java.html</artifactId>
+  <version>2.0-SNAPSHOT</version>
+  <packaging>bundle</packaging>
+  <name>HTML Context</name>
+  <url>http://maven.apache.org</url>
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <publicPackages>net.java.html,org.netbeans.html.context.spi</publicPackages>
+  </properties>
+  <build>
+      <plugins>
+          <plugin>
+              <groupId>org.apache.felix</groupId>
+              <artifactId>maven-bundle-plugin</artifactId>
+              <configuration>
+                  <instructions>
+                      <Eclipse-BuddyPolicy>dependent</Eclipse-BuddyPolicy>
+                      <Require-Capability>osgi.extender;resolution:=optional;filter:="(osgi.extender=osgi.serviceloader.processor)",osgi.serviceloader;filter:="(osgi.serviceloader=org.netbeans.html.context.spi.Contexts$Provider)";cardinality:=multiple;resolution:=optional</Require-Capability>
+                  </instructions>
+              </configuration>
+          </plugin>
+      </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>org.testng</groupId>
+      <artifactId>testng</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.netbeans.api</groupId>
+      <artifactId>org-openide-util-lookup</artifactId>
+      <scope>test</scope>
+      <type>jar</type>
+    </dependency>
+  </dependencies>
+    <description>Representation of an HTML page context a Java program operates in.</description>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/context/src/main/java/net/java/html/BrwsrCtx.java
----------------------------------------------------------------------
diff --git a/context/src/main/java/net/java/html/BrwsrCtx.java b/context/src/main/java/net/java/html/BrwsrCtx.java
new file mode 100644
index 0000000..59a4a21
--- /dev/null
+++ b/context/src/main/java/net/java/html/BrwsrCtx.java
@@ -0,0 +1,192 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html;
+
+import java.util.concurrent.Executor;
+import java.util.logging.Logger;
+import org.netbeans.html.context.impl.CtxAccssr;
+import org.netbeans.html.context.impl.CtxImpl;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.context.spi.Contexts.Id;
+
+/** Represents context where the <code>net.java.html.json.Model</code>
+ * and other objects
+ * operate in. The context is usually a particular HTML page in a browser.
+ * The context is also associated with the actual HTML technology
+ * in the HTML page - there is likely to be different context for 
+ * <a href="http://knockoutjs.com">knockout.js</a> and different one
+ * for <a href="http://angularjs.org">angular</a>. Since version 1.1
+ * the content of contexts can be selected by registering
+ * implementations under specific
+ * {@link Id technology identifiers} and requesting them during 
+ * {@link Contexts#newBuilder(java.lang.Object...) construction} of the
+ * context.
+ *
+ * @author Jaroslav Tulach
+ */
+public final class BrwsrCtx implements Executor {
+    private static final Logger LOG = Logger.getLogger(BrwsrCtx.class.getName());
+    private final CtxImpl impl;
+    private BrwsrCtx(CtxImpl impl) {
+        this.impl = impl;
+    }
+    /** currently {@link #execute(java.lang.Runnable) activated context} */
+    private static final ThreadLocal<BrwsrCtx> CURRENT = new ThreadLocal<BrwsrCtx>();
+    static {
+        new CtxAccssr() {
+            @Override
+            protected BrwsrCtx newContext(CtxImpl impl) {
+                return new BrwsrCtx(impl);
+            }
+
+            @Override
+            protected CtxImpl find(BrwsrCtx context) {
+                return context.impl;
+            }
+        };
+    }
+    /** Dummy context without binding to any real browser or technology. 
+     * Useful for simple unit testing of behavior of various business logic
+     * code.
+     */
+    public static final BrwsrCtx EMPTY = Contexts.newBuilder().build();
+    
+    
+    /** Seeks for the default context that is associated with the requesting
+     * class. If no suitable context is found, a warning message is
+     * printed and {@link #EMPTY} context is returned. One can enter 
+     * a context by calling {@link #execute(java.lang.Runnable)}.
+     * 
+     * @param requestor the class that makes the request
+     * @return appropriate context for the request
+     */
+    public static BrwsrCtx findDefault(Class<?> requestor) {
+        if (requestor == CtxAccssr.class) {
+            return EMPTY;
+        }
+        BrwsrCtx brwsr = CURRENT.get();
+        if (brwsr != null) {
+            return brwsr;
+        }
+        
+        org.netbeans.html.context.spi.Contexts.Builder cb = Contexts.newBuilder();
+        boolean found = Contexts.fillInByProviders(requestor, cb);
+        if (!found) {
+            LOG.config("No browser context found. Returning empty technology!");
+            return EMPTY;
+        }
+        return cb.build();
+    }
+
+    /** 
+     * <p>
+     * Runs provided code in the context of this {@link BrwsrCtx}.
+     * If there is an {@link Executor} {@link Contexts#find(net.java.html.BrwsrCtx, java.lang.Class)  registered in the context}
+     * it is used to perform the given code. While the code <code>exec</code>
+     * is running the value of {@link #findDefault(java.lang.Class)} returns
+     * <code>this</code>. If the executor supports a single thread execution
+     * policy, it may execute the runnable later (in such case this method
+     * returns immediately). If the call to this method is done on the right
+     * thread, the runnable should be executed synchronously.
+     * </p>
+     * <p>
+     * <b>Example Using a Timer</b>
+     * </p>
+<pre>
+<b>public final class</b> Periodicaly <b>extends</b> {@link java.util.TimerTask} {
+    <b>private final</b> {@link BrwsrCtx} ctx;
+
+    <b>private</b> Periodicaly(BrwsrCtx ctx) {
+        // remember the browser context and use it later
+        this.ctx = ctx;
+    }
+    
+    <b>public void</b> run() {
+        // arrives on wrong thread, needs to be re-scheduled
+        ctx.{@link #execute(java.lang.Runnable) execute}(new Runnable() {
+            <b>public void</b> run() {
+                // code that needs to run in a browser environment
+            }
+        });
+    }
+
+    // called when your page is ready
+    <b>public static void</b> onPageLoad(String... args) <b>throws</b> Exception {
+        // the context at the time of page initialization
+        BrwsrCtx initialCtx = BrwsrCtx.findDefault(Periodicaly.<b>class</b>);
+        // the task that is associated with context 
+        Periodicaly task = new Periodicaly(initialCtx);
+        // creates a timer
+        {@link java.util.Timer} t = new {@link java.util.Timer}("Move the box");
+        // run the task every 100ms
+        t.{@link java.util.Timer#scheduleAtFixedRate(java.util.TimerTask, long, long) scheduleAtFixedRate}(task, 0, 100);
+    }
+}
+</pre>    
+     * 
+     * @param exec the code to execute
+     * @since 0.7.6
+     */
+    @Override public final void execute(final Runnable exec) {
+        class Wrap implements Runnable {
+            @Override
+            public void run() {
+                BrwsrCtx prev = CURRENT.get();
+                try {
+                    CURRENT.set(BrwsrCtx.this);
+                    exec.run();
+                } finally {
+                    CURRENT.set(prev);
+                }
+            }
+        }
+        Wrap w = new Wrap();
+        Executor runIn = Contexts.find(this, Executor.class);
+        if (runIn == null) {
+            w.run();
+        } else {
+            runIn.execute(w);
+        }
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/context/src/main/java/net/java/html/package.html
----------------------------------------------------------------------
diff --git a/context/src/main/java/net/java/html/package.html b/context/src/main/java/net/java/html/package.html
new file mode 100644
index 0000000..007b8a0
--- /dev/null
+++ b/context/src/main/java/net/java/html/package.html
@@ -0,0 +1,49 @@
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<body>
+    <p>
+        Representation of the {@link net.java.html.BrwsrCtx browser context}.
+    </p>
+</body>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/context/src/main/java/org/netbeans/html/context/impl/CtxAccssr.java
----------------------------------------------------------------------
diff --git a/context/src/main/java/org/netbeans/html/context/impl/CtxAccssr.java b/context/src/main/java/org/netbeans/html/context/impl/CtxAccssr.java
new file mode 100644
index 0000000..641ca7a
--- /dev/null
+++ b/context/src/main/java/org/netbeans/html/context/impl/CtxAccssr.java
@@ -0,0 +1,71 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.context.impl;
+
+import net.java.html.BrwsrCtx;
+import org.netbeans.html.context.spi.Contexts.Builder;
+
+/** Internal communication between API (e.g. {@link BrwsrCtx}), SPI
+ * (e.g. {@link Builder}) and the implementation package.
+ *
+ * @author Jaroslav Tulach
+ */
+public abstract class CtxAccssr {
+    private static CtxAccssr DEFAULT;
+    static {
+        // run initializers
+        BrwsrCtx.findDefault(CtxAccssr.class);
+    }
+    
+    protected CtxAccssr() {
+        if (DEFAULT != null) throw new IllegalStateException();
+        DEFAULT = this;
+    }
+    
+    protected abstract BrwsrCtx newContext(CtxImpl impl);
+    protected abstract CtxImpl find(BrwsrCtx context);
+    
+    static CtxAccssr getDefault() {
+        return DEFAULT;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/context/src/main/java/org/netbeans/html/context/impl/CtxImpl.java
----------------------------------------------------------------------
diff --git a/context/src/main/java/org/netbeans/html/context/impl/CtxImpl.java b/context/src/main/java/org/netbeans/html/context/impl/CtxImpl.java
new file mode 100644
index 0000000..a5c64b6
--- /dev/null
+++ b/context/src/main/java/org/netbeans/html/context/impl/CtxImpl.java
@@ -0,0 +1,139 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.context.impl;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import net.java.html.BrwsrCtx;
+import org.netbeans.html.context.spi.Contexts;
+
+/** Implementation detail. Holds list of technologies for particular
+ * {@link BrwsrCtx}.
+ *
+ * @author Jaroslav Tulach
+ */
+public final class CtxImpl {
+    private final List<Bind<?>> techs;
+    private final Object[] context;
+    
+    public CtxImpl(Object[] context) {
+        this(context, new ArrayList<Bind<?>>());
+    }
+    
+    private CtxImpl(Object[] context, List<Bind<?>> techs) {
+        this.techs = techs;
+        this.context = context;
+    }
+    
+    public static <Tech> Tech find(BrwsrCtx context, Class<Tech> technology) {
+        CtxImpl impl = CtxAccssr.getDefault().find(context);
+        for (Bind<?> bind : impl.techs) {
+            if (technology == bind.clazz) {
+                return technology.cast(bind.impl);
+            }
+        }
+        return null;
+    }
+
+    public BrwsrCtx build() {
+        Collections.sort(techs, new BindCompare());
+        final List<Bind<?>> arr = Collections.unmodifiableList(techs);
+        CtxImpl impl = new CtxImpl(context, arr);
+        BrwsrCtx ctx = CtxAccssr.getDefault().newContext(impl);
+        return ctx;
+    }
+
+    public <Tech> void register(Class<Tech> type, Tech impl, int priority) {
+        techs.add(new Bind<Tech>(type, impl, priority));
+    }
+    
+    private static final class Bind<Tech> {
+        private final Class<Tech> clazz;
+        private final Tech impl;
+        private final int priority;
+
+        public Bind(Class<Tech> clazz, Tech impl, int priority) {
+            this.clazz = clazz;
+            this.impl = impl;
+            this.priority = priority;
+        }
+
+        @Override
+        public String toString() {
+            return "Bind{" + "clazz=" + clazz + "@" + clazz.getClassLoader() + ", impl=" + impl + ", priority=" + priority + '}';
+        }
+    }
+    
+    private final class BindCompare implements Comparator<Bind<?>> {
+        boolean isPrefered(Bind<?> b) {
+            final Class<?> implClazz = b.impl.getClass();
+            Contexts.Id id = implClazz.getAnnotation(Contexts.Id.class);
+            if (id == null) {
+                return false;
+            }
+            for (String v : id.value()) {
+                for (Object c : context) {
+                    if (v.equals(c)) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+        
+        @Override
+        public int compare(Bind<?> o1, Bind<?> o2) {
+            boolean p1 = isPrefered(o1);
+            boolean p2 = isPrefered(o2);
+            if (p1 != p2) {
+                return p1 ? -1 : 1;
+            }
+            if (o1.priority != o2.priority) {
+                return o1.priority - o2.priority;
+            }
+            return o1.clazz.getName().compareTo(o2.clazz.getName());
+        }
+    } // end of BindCompare
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/context/src/main/java/org/netbeans/html/context/spi/Contexts.java
----------------------------------------------------------------------
diff --git a/context/src/main/java/org/netbeans/html/context/spi/Contexts.java b/context/src/main/java/org/netbeans/html/context/spi/Contexts.java
new file mode 100644
index 0000000..5d45e1e
--- /dev/null
+++ b/context/src/main/java/org/netbeans/html/context/spi/Contexts.java
@@ -0,0 +1,244 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.context.spi;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.HashSet;
+import java.util.ServiceLoader;
+import java.util.Set;
+import net.java.html.BrwsrCtx;
+import org.netbeans.html.context.impl.CtxImpl;
+
+/** Factory class to assign various technologies 
+ * to a {@link BrwsrCtx browser context}. Start with {@link #newBuilder()}
+ * and then assign technologies with {@link Builder#register(java.lang.Class, java.lang.Object, int)}
+ * method.
+ *
+ * @author Jaroslav Tulach
+ */
+public final class Contexts {
+    private Contexts() {
+    }
+
+    /** Creates new, empty builder for creation of {@link BrwsrCtx}. At the
+     * end call the {@link Builder#build()} method to generate the context.
+     * 
+     * @param context instances of various classes or names of {@link Id technologies} 
+     *    to be preferred and used in the built {@link BrwsrCtx context}.
+     * @return new instance of the builder
+     * @since 1.1
+     */
+    public static Builder newBuilder(Object... context) {
+        return new Builder(context);
+    }
+    /** Creates new, empty builder for creation of {@link BrwsrCtx}. At the
+     * end call the {@link Builder#build()} method to generate the context.
+     * Simply calls {@link #newBuilder(java.lang.Object...) newBuilder(new Object[0])}.
+     * 
+     * @return new instance of the builder
+     */
+    public static Builder newBuilder() {
+        return newBuilder(new Object[0]);
+    }
+
+    /** Seeks for the specified technology in the provided context.
+     * 
+     * @param <Tech> type of the technology
+     * @param context the context to seek in 
+     *    (previously filled with ({@link Builder#register(java.lang.Class, java.lang.Object, int)})
+     * @param technology class that identifies the technology
+     * @return the technology in the context or <code>null</code>
+     */
+    public static <Tech> Tech find(BrwsrCtx context, Class<Tech> technology) {
+        return CtxImpl.find(context, technology);
+    }
+
+    /** Seeks {@link ServiceLoader} for all registered instances of
+     * {@link Provider} and asks them to {@link Provider#fillContext(org.netbeans.html.context.spi.Contexts.Builder, java.lang.Class) fill
+     * the builder}.
+     * 
+     * @param requestor the application class for which to find the context
+     * @param cb the context builder to register technologies into
+     * @return <code>true</code>, if some instances of the provider were
+     *    found, <code>false</code> otherwise
+     * @since 0.7.6
+     */
+    public static boolean fillInByProviders(Class<?> requestor, Contexts.Builder cb) {
+        boolean found = false;
+        ClassLoader l;
+        try {
+            l = requestor.getClassLoader();
+        } catch (SecurityException ex) {
+            l = null;
+        }
+        Set<Class<?>> classes = new HashSet<Class<?>>();
+        for (Provider cp : ServiceLoader.load(Provider.class, l)) {
+            if (!classes.add(cp.getClass())) {
+                continue;
+            }
+            cp.fillContext(cb, requestor);
+            found = true;
+        }
+        try {
+            for (Provider cp : ServiceLoader.load(Provider.class, Provider.class.getClassLoader())) {
+                if (!classes.add(cp.getClass())) {
+                    continue;
+                }
+                cp.fillContext(cb, requestor);
+                found = true;
+            }
+        } catch (SecurityException ex) {
+            if (!found) {
+                throw ex;
+            }
+        }
+        if (!found) {
+            for (Provider cp : ServiceLoader.load(Provider.class)) {
+                if (!classes.add(cp.getClass())) {
+                    continue;
+                }
+                cp.fillContext(cb, requestor);
+                found = true;
+            }
+        }
+        return found;
+    }
+    
+    /** Identifies the technologies passed to {@link Builder context builder}
+     * by a name. Each implementation of a technology 
+     * {@link Builder#register(java.lang.Class, java.lang.Object, int) registered into a context}
+     * can be annotated with a name (or multiple names). Such implementation
+     * will later be 
+     * {@link Contexts#fillInByProviders(java.lang.Class, org.netbeans.html.context.spi.Contexts.Builder)  preferred during lookup}
+     * if their name(s) has been requested in when 
+     * {@link Contexts#newBuilder(java.lang.Object...)  creating a context}.
+     * @since 1.1
+     */
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target(ElementType.TYPE)
+    @Documented
+    public @interface Id {
+        /** Identifier(s) for the implementation. 
+         * 
+         * @return one of more identifiers giving this implementation name(s)
+         */
+        public String[] value();
+    }
+
+    /** Implementors of various HTML technologies should
+     * register their implementation via <code>org.openide.util.lookup.ServiceProvider</code>, so
+     * {@link ServiceLoader} can find them, when their JARs are included
+     * on the classpath of the running application.
+     *
+     * @author Jaroslav Tulach
+     */
+    public static interface Provider {
+
+        /** Register into the context if suitable technology is
+         * available for the requesting class.
+         * The provider should check if its own technology is available in current
+         * scope (e.g. proper JDK, proper browser, etc.). The provider
+         * can also find the right context depending on requestor's classloader, etc.
+         * <p>
+         * Providers should use {@link Builder} to enrich appropriately
+         * the context.
+         *
+         * @param context the context builder to fill with technologies
+         * @param requestor the application class requesting access the the HTML page
+         * @see BrwsrCtx#findDefault(java.lang.Class)
+         */
+        void fillContext(Builder context, Class<?> requestor);
+    }
+
+    /** Support for providers of new {@link BrwsrCtx}. Providers of different
+     * technologies should be of particular interest in this class. End users
+     * designing their application with existing technologies should rather
+     * point their attention to {@link BrwsrCtx} and co.
+     *
+     * @author Jaroslav Tulach
+     */
+    public static final class Builder {
+        private final CtxImpl impl;
+
+        public Builder(Object[] context) {
+            this.impl = new CtxImpl(context);
+        }
+        
+        /** Registers new technology into the context. Each technology is
+         * exactly identified by its implementation class and can be associated
+         * with (positive) priority. In case of presence of multiple technologies
+         * with the same class, the one with higher lower priority takes precedence.
+         * @param <Tech> type of technology to register
+         * @param type the real class of the technology type
+         * @param impl an instance of the technology class
+         * @param position the lower position (but higher than zero), the more important implementation
+         *    which will be consulted sooner when seeking for a {@link Contexts#find(net.java.html.BrwsrCtx, java.lang.Class)}
+         *    an implementation
+         * @return this builder
+         * @throws IllegalStateException if the position isn't higher than <code>0</code>
+         */
+        public <Tech> Builder register(Class<Tech> type, Tech impl, int position) {
+            if (impl == null) {
+                return this;
+            }
+            if (position <= 0) {
+                throw new IllegalStateException();
+            }
+            this.impl.register(type, impl, position);
+            return this;
+        }
+
+        /** Generates context based on values previously inserted into
+         * this builder.
+         *
+         * @return new, immutable instance of {@link BrwsrCtx}
+         */
+        public BrwsrCtx build() {
+            return impl.build();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/context/src/main/java/org/netbeans/html/context/spi/package.html
----------------------------------------------------------------------
diff --git a/context/src/main/java/org/netbeans/html/context/spi/package.html b/context/src/main/java/org/netbeans/html/context/spi/package.html
new file mode 100644
index 0000000..ea999bb
--- /dev/null
+++ b/context/src/main/java/org/netbeans/html/context/spi/package.html
@@ -0,0 +1,50 @@
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<body>
+    <p>
+        Service provider classes to build {@link net.java.html.BrwsrCtx}
+        instances.
+    </p>
+</body>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/context/src/test/java/net/java/html/BrwsrCtxTest.java
----------------------------------------------------------------------
diff --git a/context/src/test/java/net/java/html/BrwsrCtxTest.java b/context/src/test/java/net/java/html/BrwsrCtxTest.java
new file mode 100644
index 0000000..9253bec
--- /dev/null
+++ b/context/src/test/java/net/java/html/BrwsrCtxTest.java
@@ -0,0 +1,120 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html;
+
+import org.netbeans.html.context.spi.Contexts;
+import static org.testng.Assert.*;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class BrwsrCtxTest {
+    
+    public BrwsrCtxTest() {
+    }
+
+
+    @org.testng.annotations.Test
+    public void canSetAssociateCtx() {
+        final BrwsrCtx ctx = Contexts.newBuilder().build();
+        final boolean[] arr = { false };
+        
+        assertNotSame(BrwsrCtx.findDefault(BrwsrCtxTest.class), ctx, "Not associated yet");
+        ctx.execute(new Runnable() {
+            @Override public void run() {
+                assertSame(BrwsrCtx.findDefault(BrwsrCtxTest.class), ctx, "Once same");
+                assertSame(BrwsrCtx.findDefault(BrwsrCtxTest.class), ctx, "2nd same");
+                arr[0] = true;
+            }
+        });
+        assertNotSame(BrwsrCtx.findDefault(BrwsrCtxTest.class), ctx, "Not associated again");
+        assertTrue(arr[0], "Runnable was executed");
+    }
+    
+    
+    @Test public void defaultOrderOfRegistrations() {
+        BrwsrCtx ctx = registerRs(Contexts.newBuilder());
+        Class<? extends Runnable> clazz = Contexts.find(ctx, Runnable.class).getClass();
+        assertEquals(clazz, R1.class, "R1 is registered at value 10");
+    }
+    
+    @Test public void preferOne() {
+        BrwsrCtx ctx = registerRs(Contexts.newBuilder("one"));
+        Class<? extends Runnable> clazz = Contexts.find(ctx, Runnable.class).getClass();
+        assertEquals(clazz, R1.class, "R1 is registered at value 10");
+    }
+
+    @Test public void preferTwo() {
+        BrwsrCtx ctx = registerRs(Contexts.newBuilder("two"));
+        Class<? extends Runnable> clazz = Contexts.find(ctx, Runnable.class).getClass();
+        assertEquals(clazz, R2.class, "R2 is preferred");
+    }
+
+    @Test public void preferBoth() {
+        BrwsrCtx ctx = registerRs(Contexts.newBuilder("one", "two"));
+        Class<? extends Runnable> clazz = Contexts.find(ctx, Runnable.class).getClass();
+        assertEquals(clazz, R1.class, "R1 is registered at value 10");
+    }
+    
+    private static BrwsrCtx registerRs(Contexts.Builder b) {
+        b.register(Runnable.class, new R1(), 10);
+        b.register(Runnable.class, new R2(), 20);
+        return b.build();
+    }
+
+    @Contexts.Id("one")
+    static final class R1 implements Runnable {
+        @Override
+        public void run() {
+        }
+    }
+    @Contexts.Id("two")
+    static final class R2 implements Runnable {
+        @Override
+        public void run() {
+        }
+    }
+    
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/context/src/test/java/org/netbeans/html/context/spi/ContextsTest.java
----------------------------------------------------------------------
diff --git a/context/src/test/java/org/netbeans/html/context/spi/ContextsTest.java b/context/src/test/java/org/netbeans/html/context/spi/ContextsTest.java
new file mode 100644
index 0000000..e579a37
--- /dev/null
+++ b/context/src/test/java/org/netbeans/html/context/spi/ContextsTest.java
@@ -0,0 +1,115 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.context.spi;
+
+import javax.xml.ws.ServiceMode;
+import net.java.html.BrwsrCtx;
+import org.openide.util.lookup.ServiceProvider;
+import static org.testng.Assert.*;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class ContextsTest {
+    
+    public ContextsTest() {
+    }
+
+    @Test public void twoInstancesButOneCall() {
+        class Two implements Runnable {
+            int cnt;
+
+            @Override
+            public void run() {
+                cnt++;
+            }
+        }
+        class One implements Runnable {
+            int cnt;
+
+            @Override
+            public void run() {
+                cnt++;
+            }
+        }
+        
+        One one = new One();
+        Two two = new Two();
+        
+        CountingProvider.onNew = two;
+        CountingProvider.onFill = one;
+        
+        Contexts.Builder b = Contexts.newBuilder();
+        Contexts.fillInByProviders(ContextsTest.class, b);
+
+        assertEquals(two.cnt, 2, "Two instances created");
+        assertEquals(one.cnt, 1, "But only one call to fill");
+    }
+
+    @ServiceProvider(service = Contexts.Provider.class)
+    public static final class CountingProvider implements Contexts.Provider {
+        static Runnable onNew;
+        static Runnable onFill;
+
+        public CountingProvider() {
+            if (onNew != null) {
+                onNew.run();
+            }
+        }
+        
+        @Override
+        public void fillContext(Contexts.Builder context, Class<?> requestor) {
+            if (onFill != null) {
+                onFill.run();
+                context.register(Runnable.class, onFill, 1);
+            }
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/equinox-agentclass-hook/pom.xml
----------------------------------------------------------------------
diff --git a/equinox-agentclass-hook/pom.xml b/equinox-agentclass-hook/pom.xml
new file mode 100644
index 0000000..436c7d5
--- /dev/null
+++ b/equinox-agentclass-hook/pom.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2016 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<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">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.netbeans.html</groupId>
+        <artifactId>pom</artifactId>
+        <version>2.0-SNAPSHOT</version>
+    </parent>
+    <name>AgentClass Hook for Equinox</name>
+    <artifactId>equinox-agentclass-hook</artifactId>
+    <packaging>bundle</packaging>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Fragment-Host>org.eclipse.osgi;bundle-version="[3.8.0,4.0)"</Fragment-Host>
+                        <Import-Package />
+                    </instructions>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>2.3.2</version>
+                <configuration>
+                    <source>1.5</source>
+                    <target>1.5</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <dependencies>
+        <dependency>
+            <groupId>org.eclipse</groupId>
+            <artifactId>org.eclipse.osgi</artifactId>
+            <version>3.8.0.v20120529-1548</version>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/equinox-agentclass-hook/src/main/java/org/netbeans/html/equinox/agentclass/AgentHook.java
----------------------------------------------------------------------
diff --git a/equinox-agentclass-hook/src/main/java/org/netbeans/html/equinox/agentclass/AgentHook.java b/equinox-agentclass-hook/src/main/java/org/netbeans/html/equinox/agentclass/AgentHook.java
new file mode 100644
index 0000000..c087d87
--- /dev/null
+++ b/equinox-agentclass-hook/src/main/java/org/netbeans/html/equinox/agentclass/AgentHook.java
@@ -0,0 +1,164 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.equinox.agentclass;
+
+import java.lang.instrument.IllegalClassFormatException;
+import java.security.ProtectionDomain;
+import java.util.ArrayList;
+import java.util.logging.Logger;
+
+import org.eclipse.osgi.baseadaptor.BaseData;
+import org.eclipse.osgi.baseadaptor.HookConfigurator;
+import org.eclipse.osgi.baseadaptor.HookRegistry;
+import org.eclipse.osgi.baseadaptor.bundlefile.BundleEntry;
+import org.eclipse.osgi.baseadaptor.hooks.ClassLoadingHook;
+import org.eclipse.osgi.baseadaptor.loader.BaseClassLoader;
+import org.eclipse.osgi.baseadaptor.loader.ClasspathEntry;
+import org.eclipse.osgi.baseadaptor.loader.ClasspathManager;
+import org.eclipse.osgi.framework.adaptor.BundleProtectionDomain;
+import org.eclipse.osgi.framework.adaptor.BundleWatcher;
+import org.eclipse.osgi.framework.adaptor.ClassLoaderDelegate;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.wiring.BundleWiring;
+
+public class AgentHook implements HookConfigurator, BundleWatcher, ClassLoadingHook {
+    private static final Logger LOG = Logger.getLogger(AgentHook.class.getName());
+	private boolean all;
+	
+	@Override
+	public void addHooks(HookRegistry hookRegistry) {
+		LOG.info("Agent hook for Equinox initialized!");
+		hookRegistry.addWatcher(this);
+		hookRegistry.addClassLoadingHook(this);
+	}
+
+	@Override
+	public void watchBundle(Bundle bundle, int type) {
+		if (!all) {
+			BundleContext c = bundle.getBundleContext();
+			if (c != null) {
+				Bundle[] arr = bundle.getBundleContext().getBundles();
+				for (Bundle b : arr) {
+					agentBundle(b);
+				}
+				all = true;
+			}
+		}
+		if (type == BundleWatcher.END_ACTIVATION) {
+			agentBundle(bundle);
+		}
+	}
+
+	private void agentBundle(Bundle bundle) {
+		String agentClass = (String)bundle.getHeaders().get("Agent-Class");
+		if (agentClass != null) {
+			Class<?> agent;
+			try {
+				agent = bundle.loadClass(agentClass);
+				NbInstrumentation.registerAgent(agent.getClassLoader(), agent.getName());
+			} catch (ClassNotFoundException e) {
+				throw new IllegalStateException(e);
+			}
+		}
+	}
+
+	@Override
+	public byte[] processClass(String name, byte[] bytes,
+			ClasspathEntry ce, BundleEntry entry,
+			ClasspathManager manager) {
+        final BaseData bd = ce.getBaseData();
+        if (bd == null) {
+            return bytes;
+        }
+        final Bundle b = bd.getBundle();
+        if (b == null) {
+            return bytes;
+        }
+        BundleWiring w = (BundleWiring)b.adapt(BundleWiring.class);
+        if (w == null) {
+            return bytes;
+        }
+        ClassLoader loader = w.getClassLoader();
+		try {
+			return NbInstrumentation.patchByteCode(loader, name, ce.getDomain(), bytes);
+		} catch (IllegalClassFormatException e) {
+			return bytes;
+		}
+	}
+
+	@Override
+	public boolean addClassPathEntry(ArrayList cpEntries,
+			String cp, ClasspathManager hostmanager, BaseData sourcedata,
+			ProtectionDomain sourcedomain) {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	@Override
+	public String findLibrary(BaseData data, String libName) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public ClassLoader getBundleClassLoaderParent() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public BaseClassLoader createClassLoader(ClassLoader parent,
+			ClassLoaderDelegate delegate, BundleProtectionDomain domain,
+			BaseData data, String[] bundleclasspath) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public void initializedClassLoader(BaseClassLoader baseClassLoader,
+			BaseData data) {
+		// TODO Auto-generated method stub
+		
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/equinox-agentclass-hook/src/main/java/org/netbeans/html/equinox/agentclass/NbInstrumentation.java
----------------------------------------------------------------------
diff --git a/equinox-agentclass-hook/src/main/java/org/netbeans/html/equinox/agentclass/NbInstrumentation.java b/equinox-agentclass-hook/src/main/java/org/netbeans/html/equinox/agentclass/NbInstrumentation.java
new file mode 100644
index 0000000..de0aa63
--- /dev/null
+++ b/equinox-agentclass-hook/src/main/java/org/netbeans/html/equinox/agentclass/NbInstrumentation.java
@@ -0,0 +1,213 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.equinox.agentclass;
+
+import java.lang.instrument.ClassDefinition;
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.IllegalClassFormatException;
+import java.lang.instrument.Instrumentation;
+import java.lang.instrument.UnmodifiableClassException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.ProtectionDomain;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.jar.JarFile;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+final class NbInstrumentation implements Instrumentation {
+    private static final Logger LOG = Logger.getLogger(NbInstrumentation.class.getName());
+    private static final Object LOCK = new Object();
+    private static volatile Collection<NbInstrumentation> ACTIVE;
+
+    private final List<ClassFileTransformer> transformers = new CopyOnWriteArrayList<ClassFileTransformer>();
+    private static final ThreadLocal<Boolean> IN = new ThreadLocal<Boolean>();
+    
+    static NbInstrumentation registerAgent(ClassLoader l, String agentClassName) {
+        try {
+            return registerImpl(agentClassName, l);
+        } catch (Throwable ex) {
+            LOG.log(Level.WARNING, "Cannot register " + agentClassName, ex);
+            return null;
+        }
+    }
+    static void unregisterAgent(NbInstrumentation instr) {
+        synchronized (LOCK) {
+            if (ACTIVE != null) {
+                Collection<NbInstrumentation> clone = new HashSet<NbInstrumentation>();
+                clone.addAll(ACTIVE);
+                clone.remove(instr);
+                ACTIVE = clone;
+            }
+        }
+    }
+    private static NbInstrumentation registerImpl(String agentClassName, ClassLoader l) throws ClassNotFoundException, IllegalArgumentException, NoSuchMethodException, SecurityException, IllegalAccessException, InvocationTargetException {
+        final NbInstrumentation inst = new NbInstrumentation();
+        synchronized (LOCK) {
+            if (ACTIVE == null) {
+                ACTIVE = new HashSet<NbInstrumentation>();
+            } else {
+                Set<NbInstrumentation> s = new HashSet<NbInstrumentation>();
+                s.addAll(ACTIVE);
+                ACTIVE = s;
+            }
+            ACTIVE.add(inst);
+        }
+        Class<?> agentClass = Class.forName(agentClassName, true, l);
+        try {
+            Method m = agentClass.getMethod("agentmain", String.class, Instrumentation.class); // NOI18N
+            m.invoke(null, "", inst);
+        } catch (NoSuchMethodException ex) {
+            Method m = agentClass.getMethod("agentmain", String.class); // NOI18N
+            m.invoke(null, "");
+        }
+        return inst;
+    }
+    
+    public static byte[] patchByteCode(ClassLoader l, String className, ProtectionDomain pd, byte[] arr) throws IllegalClassFormatException {
+        if (ACTIVE == null) {
+            return arr;
+        }
+        if (Boolean.TRUE.equals(IN.get())) {
+            return arr;
+        }
+        try {
+            IN.set(Boolean.TRUE);
+            for (NbInstrumentation inst : ACTIVE) {
+                for (ClassFileTransformer t : inst.transformers) {
+                    arr = t.transform(l, className, null, pd, arr);
+                }
+            }
+        } finally {
+            IN.set(null);
+        }
+        return arr;
+    }
+    
+    //
+    // Instrumentation methods
+    //
+
+    @Override
+    public void addTransformer(ClassFileTransformer transformer, boolean canRetransform) {
+        transformers.add(transformer);
+    }
+
+    @Override
+    public void addTransformer(ClassFileTransformer transformer) {
+        transformers.add(transformer);
+    }
+
+    @Override
+    public boolean removeTransformer(ClassFileTransformer transformer) {
+        return transformers.remove(transformer);
+    }
+
+    @Override
+    public boolean isRetransformClassesSupported() {
+        return false;
+    }
+
+    @Override
+    public void retransformClasses(Class<?>... classes) throws UnmodifiableClassException {
+        throw new UnmodifiableClassException();
+    }
+
+    @Override
+    public boolean isRedefineClassesSupported() {
+        return false;
+    }
+
+    @Override
+    public void redefineClasses(ClassDefinition... definitions) throws ClassNotFoundException, UnmodifiableClassException {
+        throw new UnmodifiableClassException();
+    }
+
+    @Override
+    public boolean isModifiableClass(Class<?> theClass) {
+        return false;
+    }
+
+    @Override
+    public Class[] getAllLoadedClasses() {
+        return new Class[0];
+    }
+
+    @Override
+    public Class[] getInitiatedClasses(ClassLoader loader) {
+        return new Class[0];
+    }
+
+    @Override
+    public long getObjectSize(Object objectToSize) {
+        return 42;
+    }
+
+    @Override
+    public void appendToBootstrapClassLoaderSearch(JarFile jarfile) {
+    }
+
+    @Override
+    public void appendToSystemClassLoaderSearch(JarFile jarfile) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isNativeMethodPrefixSupported() {
+        return false;
+    }
+
+    @Override
+    public void setNativeMethodPrefix(ClassFileTransformer transformer, String prefix) {
+        throw new UnsupportedOperationException();
+    }
+    
+}



[24/24] incubator-netbeans-html4j git commit: [INFRA-15006] Initial donation of HTML/Java NetBeans API. Equivalent to the content of html4j-donation-review.zip donated as part of ApacheNetBeansDonation1.zip with SHA256 being 7f2ca0f61953a190613c9a0fbcc1b

Posted by jt...@apache.org.
[INFRA-15006] Initial donation of HTML/Java NetBeans API.
Equivalent to the content of html4j-donation-review.zip donated as part of ApacheNetBeansDonation1.zip with SHA256 being 7f2ca0f61953a190613c9a0fbcc1b034084b04a4d55d23c02cefffc354e7c24a.
Equivalent to changeset 929563230c07 of the original http://hg.netbeans.org/html4j/ NetBeans Hg repository.
Equivalent to commit d029b8e in the Emilian Bold Git conversion https://github.com/emilianbold/netbeans-html4j/ of the above Hg repository.


Project: http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/commit/226089a5
Tree: http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/tree/226089a5
Diff: http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/diff/226089a5

Branch: refs/heads/master
Commit: 226089a5a86d173bcc8acec247bd9def277e1459
Parents: 
Author: Jaroslav Tulach <ja...@apidesign.org>
Authored: Sun Sep 3 14:47:24 2017 +0200
Committer: Jaroslav Tulach <ja...@apidesign.org>
Committed: Sun Sep 3 14:47:24 2017 +0200

----------------------------------------------------------------------
 COPYING                                         |   40 +
 boot-agent-test/pom.xml                         |  105 +
 .../html/bootagent/DynamicClassLoaderTest.java  |  126 +
 .../html/bootagent/JavaScriptBodyTst.java       |   63 +
 .../java/org/netbeans/html/bootagent/KOFx.java  |  125 +
 .../org/netbeans/html/bootagent/empty.html      |   55 +
 boot-fx/pom.xml                                 |  115 +
 .../java/net/java/html/boot/fx/FXBrowsers.java  |  293 +++
 .../java/net/java/html/boot/fx/package.html     |   51 +
 .../html/boot/fx/AbstractFXPresenter.java       |  433 ++++
 .../java/org/netbeans/html/boot/fx/Dbgr.java    |   87 +
 .../java/org/netbeans/html/boot/fx/FXBrwsr.java |  427 ++++
 .../org/netbeans/html/boot/fx/FXConsole.java    |   84 +
 .../org/netbeans/html/boot/fx/FXInspect.java    |  134 +
 .../org/netbeans/html/boot/fx/FXPresenter.java  |   88 +
 .../org/netbeans/html/boot/fx/FXToolbar.java    |  437 ++++
 .../org/netbeans/html/boot/fx/WatchDir.java     |  117 +
 .../org/netbeans/html/boot/fx/Bundle.properties |   54 +
 .../org/netbeans/html/boot/fx/desktop.png       |  Bin 0 -> 1093 bytes
 .../netbeans/html/boot/fx/handheldLandscape.png |  Bin 0 -> 1100 bytes
 .../netbeans/html/boot/fx/handheldPortrait.png  |  Bin 0 -> 1111 bytes
 .../org/netbeans/html/boot/fx/netbook.png       |  Bin 0 -> 1101 bytes
 .../org/netbeans/html/boot/fx/selectionMode.png |  Bin 0 -> 1078 bytes
 .../org/netbeans/html/boot/fx/sizeToFit.png     |  Bin 0 -> 1093 bytes
 .../netbeans/html/boot/fx/tabletLandscape.png   |  Bin 0 -> 1076 bytes
 .../netbeans/html/boot/fx/tabletPortrait.png    |  Bin 0 -> 1067 bytes
 .../org/netbeans/html/boot/fx/widescreen.png    |  Bin 0 -> 1114 bytes
 .../html/boot/fx/FXBrowsersOnResourceTest.java  |  209 ++
 .../net/java/html/boot/fx/FXBrowsersTest.java   |  253 ++
 .../org/netbeans/html/boot/fx/FXBrwsrTest.java  |   84 +
 .../netbeans/html/boot/fx/FXJavaScriptTest.java |  124 +
 .../netbeans/html/boot/fx/FXPresenterTst.java   |  103 +
 .../netbeans/html/boot/fx/FxJavaScriptTst.java  |   55 +
 .../java/org/netbeans/html/boot/fx/KOFx.java    |  125 +
 .../org/netbeans/html/boot/fx/ReloadTest.java   |  157 ++
 .../netbeans/html/boot/fx/TestingProvider.java  |   65 +
 .../org/netbeans/html/boot/fx/TitleTest.java    |  146 ++
 .../test/java/org/sample/app/pkg/SampleApp.java |   62 +
 .../test/resources/net/java/html/boot/fx/wnd.js |   49 +
 .../org/netbeans/html/boot/fx/empty.html        |   55 +
 boot-script/pom.xml                             |  152 ++
 .../java/html/boot/script/ScriptPresenter.java  |  443 ++++
 .../java/net/java/html/boot/script/Scripts.java |  119 +
 .../java/net/java/html/boot/script/package.html |   51 +
 .../html/boot/script/Jsr223JavaScriptTest.java  |  117 +
 .../html/boot/script/Jsr223JavaScriptTst.java   |   55 +
 .../net/java/html/boot/script/SingleCase.java   |  127 +
 .../java/html/boot/script/ko4j/DynamicHTTP.java |  261 ++
 .../net/java/html/boot/script/ko4j/KOCase.java  |  140 ++
 .../boot/script/ko4j/KnockoutEnvJSTest.java     |  266 ++
 .../net/java/html/boot/script/ko4j/test.html    |   56 +
 boot-truffle/empty.sigtest                      |   21 +
 boot-truffle/pom.xml                            |  209 ++
 .../net/java/html/boot/truffle/IsArrayNode.java |   68 +
 .../net/java/html/boot/truffle/IsNullNode.java  |   68 +
 .../net/java/html/boot/truffle/JavaArray.java   |  102 +
 .../net/java/html/boot/truffle/JavaObject.java  |   82 +
 .../net/java/html/boot/truffle/JavaValue.java   |   91 +
 .../html/boot/truffle/TrufflePresenter.java     |  293 +++
 .../html/boot/truffle/TrufflePresenters.java    |   66 +
 .../net/java/html/boot/truffle/package.html     |   51 +
 .../html/boot/truffle/JsArrayTruffleTest.java   |  140 ++
 .../net/java/html/boot/truffle/SingleCase.java  |  127 +
 .../boot/truffle/TruffleJavaScriptTest.java     |  164 ++
 boot/pom.xml                                    |  106 +
 .../java/net/java/html/boot/BrowserBuilder.java |  519 ++++
 .../main/java/net/java/html/boot/package.html   |   54 +
 .../java/net/java/html/js/JavaScriptBody.java   |  152 ++
 .../net/java/html/js/JavaScriptResource.java    |   70 +
 .../src/main/java/net/java/html/js/package.html |  483 ++++
 .../netbeans/html/boot/impl/FindResources.java  |   56 +
 .../org/netbeans/html/boot/impl/FnContext.java  |  165 ++
 .../org/netbeans/html/boot/impl/FnUtils.java    |  717 ++++++
 .../html/boot/impl/JavaScriptProcesor.java      |  528 ++++
 .../org/netbeans/html/boot/impl/JsAgent.java    |   75 +
 .../org/netbeans/html/boot/impl/JsCallback.java |  160 ++
 .../netbeans/html/boot/impl/JsClassLoader.java  |   57 +
 .../org/netbeans/html/boot/impl/JsPkgCache.java |  132 +
 .../java/org/netbeans/html/boot/spi/Fn.java     |  388 +++
 .../org/netbeans/html/boot/spi/package.html     |   54 +
 .../resources/net/java/html/boot/html4j.txt     |   43 +
 .../net/java/html/boot/BrowserBuilderTest.java  |  134 +
 .../org/netbeans/html/boot/impl/Arithm.java     |   53 +
 .../org/netbeans/html/boot/impl/Compile.java    |  307 +++
 .../html/boot/impl/CountFnCreationTest.java     |  123 +
 .../org/netbeans/html/boot/impl/FnTest.java     |  186 ++
 .../html/boot/impl/JavaScriptProcesorTest.java  |  167 ++
 .../netbeans/html/boot/impl/JsCallbackTest.java |   86 +
 .../html/boot/impl/JsClassLoaderBase.java       |  285 +++
 .../html/boot/impl/JsClassLoaderTest.java       |  161 ++
 .../org/netbeans/html/boot/impl/JsMethods.java  |  157 ++
 .../netbeans/html/boot/impl/KeepAliveTest.java  |  113 +
 .../org/netbeans/html/boot/impl/Object.java     |   49 +
 .../org/netbeans/html/boot/impl/empty.js        |   42 +
 .../org/netbeans/html/boot/impl/jsmethods.js    |   43 +
 context/pom.xml                                 |   91 +
 .../src/main/java/net/java/html/BrwsrCtx.java   |  192 ++
 .../src/main/java/net/java/html/package.html    |   49 +
 .../netbeans/html/context/impl/CtxAccssr.java   |   71 +
 .../org/netbeans/html/context/impl/CtxImpl.java |  139 ++
 .../org/netbeans/html/context/spi/Contexts.java |  244 ++
 .../org/netbeans/html/context/spi/package.html  |   50 +
 .../test/java/net/java/html/BrwsrCtxTest.java   |  120 +
 .../netbeans/html/context/spi/ContextsTest.java |  115 +
 equinox-agentclass-hook/pom.xml                 |   86 +
 .../html/equinox/agentclass/AgentHook.java      |  164 ++
 .../equinox/agentclass/NbInstrumentation.java   |  213 ++
 .../main/resources/hookconfigurators.properties |   44 +
 geo/pom.xml                                     |   94 +
 .../main/java/net/java/html/geo/OnLocation.java |   96 +
 .../main/java/net/java/html/geo/Position.java   |  383 +++
 .../net/java/html/geo/doc-files/GeoDuke.png     |  Bin 0 -> 242819 bytes
 .../main/java/net/java/html/geo/package.html    |   74 +
 .../org/netbeans/html/geo/impl/Accessor.java    |   76 +
 .../netbeans/html/geo/impl/GeoProcessor.java    |  290 +++
 .../netbeans/html/geo/impl/JsGLProvider.java    |  154 ++
 .../org/netbeans/html/geo/spi/CoordImpl.java    |   87 +
 .../org/netbeans/html/geo/spi/GLProvider.java   |  306 +++
 .../java/org/netbeans/html/geo/spi/package.html |   59 +
 .../java/net/java/html/geo/OnLocationTest.java  |  137 ++
 .../org/netbeans/html/geo/impl/Compile.java     |  286 +++
 .../html/geo/impl/GeoProcessorTest.java         |  114 +
 .../html/geo/impl/JsGLProviderTest.java         |   76 +
 .../netbeans/html/geo/spi/CoordImplTest.java    |  106 +
 html4j-maven-plugin/pom.xml                     |  119 +
 .../html/mojo/ProcessJsAnnotationsMojo.java     |  223 ++
 json-tck/pom.xml                                |  116 +
 .../java/net/java/html/js/tests/Bodies.java     |  254 ++
 .../java/net/java/html/js/tests/Factorial.java  |   62 +
 .../java/net/java/html/js/tests/GCBodyTest.java |  178 ++
 .../net/java/html/js/tests/Global2String.java   |   52 +
 .../net/java/html/js/tests/GlobalString.java    |   52 +
 .../java/html/js/tests/JavaScriptBodyTest.java  |  548 +++++
 .../main/java/net/java/html/js/tests/Later.java |   65 +
 .../java/net/java/html/js/tests/Receiver.java   |   80 +
 .../main/java/net/java/html/js/tests/Sum.java   |   81 +
 .../java/html/json/tests/ConvertTypesTest.java  |  322 +++
 .../java/html/json/tests/GCKnockoutTest.java    |  139 ++
 .../java/net/java/html/json/tests/JSONTest.java |  637 +++++
 .../net/java/html/json/tests/KnockoutTest.java  | 1001 ++++++++
 .../net/java/html/json/tests/MinesTest.java     |  356 +++
 .../java/html/json/tests/OperationsTest.java    |   96 +
 .../net/java/html/json/tests/PairModel.java     |   80 +
 .../net/java/html/json/tests/PersonImpl.java    |  100 +
 .../main/java/net/java/html/json/tests/Sex.java |   51 +
 .../java/net/java/html/json/tests/Utils.java    |  225 ++
 .../net/java/html/json/tests/WebSocketTest.java |  179 ++
 .../netbeans/html/json/tck/JavaScriptTCK.java   |   73 +
 .../java/org/netbeans/html/json/tck/KOTest.java |   60 +
 .../org/netbeans/html/json/tck/KnockoutTCK.java |  144 ++
 .../resources/net/java/html/js/tests/global.js  |   44 +
 .../resources/net/java/html/js/tests/global2.js |   44 +
 .../org/netbeans/html/json/tck/package.html     |   56 +
 .../java/net/java/html/js/tests/BodiesTest.java |   83 +
 json/pom.xml                                    |  115 +
 .../net/java/html/json/ComputedProperty.java    |  105 +
 .../main/java/net/java/html/json/FakeModel.java |  122 +
 .../main/java/net/java/html/json/Function.java  |  122 +
 .../src/main/java/net/java/html/json/Model.java |  356 +++
 .../java/net/java/html/json/ModelOperation.java |  100 +
 .../main/java/net/java/html/json/Models.java    |  191 ++
 .../net/java/html/json/OnPropertyChange.java    |  100 +
 .../main/java/net/java/html/json/OnReceive.java |  204 ++
 .../main/java/net/java/html/json/Property.java  |   98 +
 .../net/java/html/json/doc-files/DukeHTML.png   |  Bin 0 -> 70129 bytes
 .../net/java/html/json/doc-files/html4j.png     |  Bin 0 -> 558910 bytes
 .../java/html/json/doc-files/websockets.html    |  185 ++
 .../main/java/net/java/html/json/package.html   |  118 +
 .../org/netbeans/html/json/impl/Bindings.java   |  133 +
 .../java/org/netbeans/html/json/impl/JSON.java  |  533 ++++
 .../org/netbeans/html/json/impl/JSONList.java   |  257 ++
 .../netbeans/html/json/impl/ModelProcessor.java | 2286 ++++++++++++++++++
 .../html/json/impl/PropertyBindingAccessor.java |  109 +
 .../org/netbeans/html/json/impl/RcvrJSON.java   |  136 ++
 .../org/netbeans/html/json/impl/Transitive.java |   60 +
 .../netbeans/html/json/spi/FunctionBinding.java |  162 ++
 .../org/netbeans/html/json/spi/JSONCall.java    |  152 ++
 .../org/netbeans/html/json/spi/Observers.java   |  233 ++
 .../netbeans/html/json/spi/PropertyBinding.java |  236 ++
 .../java/org/netbeans/html/json/spi/Proto.java  |  953 ++++++++
 .../org/netbeans/html/json/spi/Technology.java  |  216 ++
 .../org/netbeans/html/json/spi/Transfer.java    |   96 +
 .../org/netbeans/html/json/spi/WSTransfer.java  |   92 +
 .../org/netbeans/html/json/spi/package.html     |   51 +
 .../netbeans/html/json/impl/Bundle.properties   |   97 +
 .../org/netbeans/html/json/spi/package.html     |   59 +
 .../java/net/java/html/json/AdressTest.java     |   59 +
 .../test/java/net/java/html/json/BoardTest.java |   83 +
 .../net/java/html/json/BooleanArrayTest.java    |   80 +
 .../test/java/net/java/html/json/Compile.java   |  311 +++
 .../java/net/java/html/json/KeywordsTest.java   |   99 +
 .../java/html/json/MapModelNotMutableTest.java  |  227 ++
 .../java/net/java/html/json/MapModelTest.java   |  521 ++++
 .../net/java/html/json/ModelProcessorTest.java  |  823 +++++++
 .../test/java/net/java/html/json/ModelTest.java |  506 ++++
 .../java/net/java/html/json/ModelsTest.java     |   72 +
 .../java/net/java/html/json/OperationTest.java  |  125 +
 .../java/net/java/html/json/PersonImpl.java     |  135 ++
 .../net/java/html/json/PrimitiveArrayTest.java  |   80 +
 json/src/test/java/net/java/html/json/Sex.java  |   51 +
 .../test/java/net/java/html/json/TypesTest.java |  145 ++
 .../java/net/java/html/json/UnderscoreTest.java |   67 +
 .../net/java/html/json/WebSocketCallTest.java   |   56 +
 .../net/java/html/json/sub/StreetCntrl.java     |   53 +
 .../netbeans/html/json/impl/BuilderTest.java    |  117 +
 .../html/json/impl/ConstructorTest.java         |   80 +
 .../netbeans/html/json/impl/DeepChangeTest.java |  617 +++++
 .../netbeans/html/json/impl/EmployeeImpl.java   |  119 +
 .../netbeans/html/json/impl/EmployerTest.java   |   65 +
 .../netbeans/html/json/impl/InfinityTest.java   |  151 ++
 .../netbeans/html/json/impl/JSONListTest.java   |  236 ++
 .../org/netbeans/html/json/impl/JSONTest.java   |   84 +
 .../html/json/impl/NoPropertiesTest.java        |   55 +
 .../netbeans/html/json/impl/OnReceiveTest.java  |  180 ++
 .../html/json/impl/ParallelChangeTest.java      |  199 ++
 .../org/netbeans/html/json/impl/ToDoTest.java   |  132 +
 ko-felix-test/pom.xml                           |  184 ++
 .../ko/felix/test/KnockoutFelixTCKImpl.java     |  283 +++
 .../html/ko/felix/test/DynamicHTTP.java         |  261 ++
 .../org/netbeans/html/ko/felix/test/KOFx.java   |  132 +
 .../ko/felix/test/KnockoutFelixAriesIT.java     |  258 ++
 .../html/ko/felix/test/KnockoutFelixIT.java     |  253 ++
 .../org/netbeans/html/ko/felix/test/test.html   |   56 +
 ko-osgi-test/pom.xml                            |  181 ++
 .../ko/osgi/test/KnockoutEquinoxTCKImpl.java    |  222 ++
 .../netbeans/html/ko/osgi/test/DynamicHTTP.java |  261 ++
 .../org/netbeans/html/ko/osgi/test/KOFx.java    |  130 +
 .../html/ko/osgi/test/KnockoutEquinoxIT.java    |  245 ++
 .../org/netbeans/html/ko/osgi/test/test.html    |   56 +
 ko-ws-tyrus/pom.xml                             |  194 ++
 .../org/netbeans/html/wstyrus/LoadJSON.java     |  301 +++
 .../org/netbeans/html/wstyrus/TyrusContext.java |  209 ++
 .../netbeans/html/wstyrus/TyrusDynamicHTTP.java |  262 ++
 .../java/org/netbeans/html/wstyrus/TyrusFX.java |  125 +
 .../html/wstyrus/TyrusKnockoutTest.java         |  219 ++
 .../org/netbeans/html/wstyrus/test.html         |   56 +
 ko4j/pom.xml                                    |  179 ++
 .../main/java/org/netbeans/html/ko4j/KO4J.java  |  149 ++
 .../java/org/netbeans/html/ko4j/KOSockets.java  |   81 +
 .../java/org/netbeans/html/ko4j/KOTech.java     |  176 ++
 .../java/org/netbeans/html/ko4j/KOTransfer.java |  164 ++
 .../java/org/netbeans/html/ko4j/Knockout.java   |  277 +++
 .../java/org/netbeans/html/ko4j/LoadJSON.java   |  138 ++
 .../java/org/netbeans/html/ko4j/LoadWS.java     |  141 ++
 .../org/netbeans/html/ko4j/DynamicHTTP.java     |  261 ++
 .../html/ko4j/InitializeKnockoutTest.java       |  156 ++
 .../test/java/org/netbeans/html/ko4j/KOFx.java  |  132 +
 .../org/netbeans/html/ko4j/KnockoutFXTest.java  |  235 ++
 .../netbeans/html/ko4j/LessCallbacksCheck.java  |   97 +
 .../html/ko4j/OffThreadInitializationTest.java  |  166 ++
 .../html/ko4j/ReferenceKnockoutTest.java        |   63 +
 .../resources/org/netbeans/html/ko4j/test.html  |   56 +
 pom.xml                                         |  475 ++++
 sound/pom.xml                                   |   93 +
 .../java/net/java/html/sound/AudioClip.java     |  200 ++
 .../main/java/net/java/html/sound/package.html  |   50 +
 .../html/sound/impl/BrowserAudioEnv.java        |   85 +
 .../html/sound/spi/AudioEnvironment.java        |   94 +
 .../org/netbeans/html/sound/spi/package.html    |   49 +
 src/main/javadoc/overview.html                  |  623 +++++
 xhr4j/pom.xml                                   |  166 ++
 .../java/org/netbeans/html/xhr4j/LoadJSON.java  |  274 +++
 .../html/xhr4j/XmlHttpResourceContext.java      |   88 +
 .../netbeans/html/xhr4j/JsonDynamicHTTP.java    |  262 ++
 .../java/org/netbeans/html/xhr4j/JsonFX.java    |  125 +
 .../netbeans/html/xhr4j/JsonKnockoutTest.java   |  217 ++
 .../resources/org/netbeans/html/xhr4j/test.html |   56 +
 267 files changed, 45112 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/COPYING
----------------------------------------------------------------------
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..83fe966
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,40 @@
+DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+
+Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+Other names may be trademarks of their respective owners.
+
+The contents of this file are subject to the terms of either the GNU
+General Public License Version 2 only ("GPL") or the Common
+Development and Distribution License("CDDL") (collectively, the
+"License"). You may not use this file except in compliance with the
+License. You can obtain a copy of the License at
+http://www.netbeans.org/cddl-gplv2.html
+or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+specific language governing permissions and limitations under the
+License.  When distributing the software, include this License Header
+Notice in each file and include the License file at
+nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+particular file as subject to the "Classpath" exception as provided
+by Oracle in the GPL Version 2 section of the License file that
+accompanied this code. If applicable, add the following below the
+License Header, with the fields enclosed by brackets [] replaced by
+your own identifying information:
+"Portions Copyrighted [year] [name of copyright owner]"
+
+Contributor(s):
+
+The Original Software is NetBeans. The Initial Developer of the Original
+Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+If you wish your version of this file to be governed by only the CDDL
+or only the GPL Version 2, indicate your decision by adding
+"[Contributor] elects to include this software in this distribution
+under the [CDDL or GPL Version 2] license." If you do not indicate a
+single choice of license, a recipient has the option to distribute
+your version of this file under either the CDDL, the GPL Version 2 or
+to extend the choice of license to its licensees as provided above.
+However, if you add GPL Version 2 code and therefore, elected the GPL
+Version 2 license, then the option applies only if the new code is
+made subject to such option by the copyright holder.

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-agent-test/pom.xml
----------------------------------------------------------------------
diff --git a/boot-agent-test/pom.xml b/boot-agent-test/pom.xml
new file mode 100644
index 0000000..8159679
--- /dev/null
+++ b/boot-agent-test/pom.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2016 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<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">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.netbeans.html</groupId>
+        <artifactId>pom</artifactId>
+        <version>2.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>boot-agent-test</artifactId>
+    <packaging>jar</packaging>
+    <name>Dynamic Boot Test</name>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-deploy-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <dependencies>
+        <dependency>
+            <groupId>org.testng</groupId>
+            <artifactId>testng</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.netbeans.html</groupId>
+            <artifactId>net.java.html</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>org.netbeans.html</groupId>
+            <artifactId>net.java.html.boot</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>org.netbeans.api</groupId>
+            <artifactId>org-openide-util-lookup</artifactId>
+            <scope>test</scope>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>net.java.html.boot.fx</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ow2.asm</groupId>
+            <artifactId>asm</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-agent-test/src/test/java/org/netbeans/html/bootagent/DynamicClassLoaderTest.java
----------------------------------------------------------------------
diff --git a/boot-agent-test/src/test/java/org/netbeans/html/bootagent/DynamicClassLoaderTest.java b/boot-agent-test/src/test/java/org/netbeans/html/bootagent/DynamicClassLoaderTest.java
new file mode 100644
index 0000000..610834d
--- /dev/null
+++ b/boot-agent-test/src/test/java/org/netbeans/html/bootagent/DynamicClassLoaderTest.java
@@ -0,0 +1,126 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.bootagent;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executors;
+import net.java.html.BrwsrCtx;
+import net.java.html.boot.BrowserBuilder;
+import org.netbeans.html.boot.spi.Fn;
+import org.testng.Assert;
+import static org.testng.Assert.assertNotSame;
+import static org.testng.Assert.assertSame;
+import org.testng.annotations.Factory;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class DynamicClassLoaderTest {
+    private static Class<?> browserClass;
+    private static Fn.Presenter browserPresenter;
+    
+    public DynamicClassLoaderTest() {
+    }
+
+    @Factory public static Object[] compatibilityTests() throws Exception {
+        final BrowserBuilder bb = BrowserBuilder.newBrowser().loadClass(DynamicClassLoaderTest.class).
+            loadPage("empty.html").
+            invoke("initialized");
+
+        Executors.newSingleThreadExecutor().submit(new Runnable() {
+            @Override
+            public void run() {
+                bb.showAndWait();
+            }
+        });
+
+        List<Object> res = new ArrayList<Object>();
+
+        Class[] arr = new Class[] { loadClass() };
+        for (Class c : arr) {
+            for (Method m : c.getDeclaredMethods()) {
+                if ((m.getModifiers() & Modifier.PUBLIC) != 0) {
+                    res.add(new KOFx(browserPresenter, m));
+                }
+            }
+        }
+        return res.toArray();
+    }
+
+    static synchronized Class<?> loadClass() throws InterruptedException {
+        while (browserClass == null) {
+            DynamicClassLoaderTest.class.wait();
+        }
+        return browserClass;
+    }
+    
+    public static void ready(Class<?> browserCls) throws Exception {
+        Class<?> origClazz = ClassLoader.getSystemClassLoader().loadClass(DynamicClassLoaderTest.class.getName());
+        final Field f1 = origClazz.getDeclaredField("browserClass");
+        f1.setAccessible(true);
+        f1.set(null, browserCls);
+        final Field f2 = origClazz.getDeclaredField("browserPresenter");
+        f2.setAccessible(true);
+        f2.set(null, Fn.activePresenter());
+        synchronized (origClazz) {
+            origClazz.notifyAll();
+        }
+    }
+    
+    public static void initialized() throws Exception {
+        BrwsrCtx b1 = BrwsrCtx.findDefault(DynamicClassLoaderTest.class);
+        assertNotSame(b1, BrwsrCtx.EMPTY, "Browser context is not empty");
+        BrwsrCtx b2 = BrwsrCtx.findDefault(DynamicClassLoaderTest.class);
+        assertSame(b1, b2, "Browser context remains stable");
+        Assert.assertNotSame(DynamicClassLoaderTest.class.getClassLoader(),
+            ClassLoader.getSystemClassLoader(),
+            "Should use special classloader, not system one"
+        );
+        DynamicClassLoaderTest.ready(JavaScriptBodyTst.class);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-agent-test/src/test/java/org/netbeans/html/bootagent/JavaScriptBodyTst.java
----------------------------------------------------------------------
diff --git a/boot-agent-test/src/test/java/org/netbeans/html/bootagent/JavaScriptBodyTst.java b/boot-agent-test/src/test/java/org/netbeans/html/bootagent/JavaScriptBodyTst.java
new file mode 100644
index 0000000..33c2d22
--- /dev/null
+++ b/boot-agent-test/src/test/java/org/netbeans/html/bootagent/JavaScriptBodyTst.java
@@ -0,0 +1,63 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.bootagent;
+
+import net.java.html.js.JavaScriptBody;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class JavaScriptBodyTst {
+    
+    public JavaScriptBodyTst() {
+    }
+
+    public void assert42() {
+        int v = mul(7, 6);
+        assert v == 42 : "Really 42: " + v;
+    }
+    
+    @JavaScriptBody(args = { "x", "y" }, body = "return x * y;")
+    private static native int mul(int x, int y);
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-agent-test/src/test/java/org/netbeans/html/bootagent/KOFx.java
----------------------------------------------------------------------
diff --git a/boot-agent-test/src/test/java/org/netbeans/html/bootagent/KOFx.java b/boot-agent-test/src/test/java/org/netbeans/html/bootagent/KOFx.java
new file mode 100644
index 0000000..19f5c5a
--- /dev/null
+++ b/boot-agent-test/src/test/java/org/netbeans/html/bootagent/KOFx.java
@@ -0,0 +1,125 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.bootagent;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import javafx.application.Platform;
+import org.netbeans.html.boot.impl.FnContext;
+import org.netbeans.html.boot.spi.Fn;
+import org.testng.IHookCallBack;
+import org.testng.IHookable;
+import org.testng.ITest;
+import org.testng.ITestResult;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public final class KOFx implements ITest, IHookable, Runnable {
+    private final Fn.Presenter p;
+    private final Method m;
+    private Object result;
+    private Object inst;
+
+    KOFx(Fn.Presenter p, Method m) {
+        this.p = p;
+        this.m = m;
+    }
+
+    @Override
+    public String getTestName() {
+        return m.getName();
+    }
+
+    @Test
+    public synchronized void executeTest() throws Exception {
+        if (result == null) {
+            Platform.runLater(this);
+            wait();
+        }
+        if (result instanceof Exception) {
+            throw (Exception)result;
+        }
+        if (result instanceof Error) {
+            throw (Error)result;
+        }
+    }
+
+    @Override
+    public synchronized void run() {
+        boolean notify = true;
+        try {
+            FnContext.currentPresenter(p);
+            if (inst == null) {
+                inst = m.getDeclaringClass().newInstance();
+            }
+            result = m.invoke(inst);
+            if (result == null) {
+                result = this;
+            }
+        } catch (InvocationTargetException ex) {
+            Throwable r = ex.getTargetException();
+            if (r instanceof InterruptedException) {
+                notify = false;
+                Platform.runLater(this);
+                return;
+            }
+            result = r;
+        } catch (Exception ex) {
+            result = ex;
+        } finally {
+            if (notify) {
+                notifyAll();
+            }
+            FnContext.currentPresenter(null);
+        }
+    }
+
+    @Override
+    public void run(IHookCallBack ihcb, ITestResult itr) {
+        ihcb.runTestMethod(itr);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-agent-test/src/test/resources/org/netbeans/html/bootagent/empty.html
----------------------------------------------------------------------
diff --git a/boot-agent-test/src/test/resources/org/netbeans/html/bootagent/empty.html b/boot-agent-test/src/test/resources/org/netbeans/html/bootagent/empty.html
new file mode 100644
index 0000000..bf2114b
--- /dev/null
+++ b/boot-agent-test/src/test/resources/org/netbeans/html/bootagent/empty.html
@@ -0,0 +1,55 @@
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>Bootstrap Dynamically</title>
+        <meta charset="UTF-8">
+        <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    </head>
+    <body>
+        <div>Bootstrap Dynamically</div>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/pom.xml
----------------------------------------------------------------------
diff --git a/boot-fx/pom.xml b/boot-fx/pom.xml
new file mode 100644
index 0000000..26dd2ce
--- /dev/null
+++ b/boot-fx/pom.xml
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2016 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<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">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.netbeans.html</groupId>
+    <artifactId>pom</artifactId>
+    <version>2.0-SNAPSHOT</version>
+  </parent>
+  <groupId>org.netbeans.html</groupId>
+  <artifactId>net.java.html.boot.fx</artifactId>
+  <version>2.0-SNAPSHOT</version>
+  <name>FX WebView Bootstrap</name>
+  <packaging>bundle</packaging>
+  <url>http://maven.apache.org</url>
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <publicPackages>net.java.html.boot.fx</publicPackages>
+  </properties>
+  <build>
+      <plugins>
+          <plugin>
+              <groupId>org.apache.felix</groupId>
+              <artifactId>maven-bundle-plugin</artifactId>
+              <configuration>
+                  <instructions>
+                      <Require-Capability>osgi.extender;resolution:=optional;filter:="(osgi.extender=osgi.serviceloader.registrar)"</Require-Capability>
+                      <Provide-Capability>osgi.serviceloader;osgi.serviceloader=org.netbeans.html.boot.spi.Fn$Presenter</Provide-Capability>
+                  </instructions>
+              </configuration>
+          </plugin>
+          <plugin>
+              <groupId>org.netbeans.html</groupId>
+              <artifactId>html4j-maven-plugin</artifactId>
+          </plugin>
+      </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+        <groupId>com.oracle</groupId>
+        <artifactId>javafx</artifactId>
+        <version>2.2</version>
+        <scope>system</scope>
+        <systemPath>${jfxrt.jar}</systemPath>
+    </dependency>
+    <dependency>
+      <groupId>org.netbeans.api</groupId>
+      <artifactId>org-openide-util-lookup</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.netbeans.html</groupId>
+      <artifactId>net.java.html.boot</artifactId>
+      <version>${project.version}</version>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+      <groupId>org.testng</groupId>
+      <artifactId>testng</artifactId>
+      <scope>test</scope>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>net.java.html.json.tck</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+    <description>A presentation provider to show JavaFX WebView 
+when a Java/HTML based application is about to boot.</description>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/java/net/java/html/boot/fx/FXBrowsers.java
----------------------------------------------------------------------
diff --git a/boot-fx/src/main/java/net/java/html/boot/fx/FXBrowsers.java b/boot-fx/src/main/java/net/java/html/boot/fx/FXBrowsers.java
new file mode 100644
index 0000000..eca28e1
--- /dev/null
+++ b/boot-fx/src/main/java/net/java/html/boot/fx/FXBrowsers.java
@@ -0,0 +1,293 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.boot.fx;
+
+import java.net.URL;
+import javafx.application.Platform;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.concurrent.Worker;
+import javafx.scene.web.WebView;
+import net.java.html.BrwsrCtx;
+import net.java.html.boot.BrowserBuilder;
+import net.java.html.js.JavaScriptBody;
+import org.netbeans.html.boot.fx.AbstractFXPresenter;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.context.spi.Contexts.Id;
+
+/** Utility methods to use {@link WebView} and {@link JavaScriptBody} code
+ * in existing <em>JavaFX</em> applications.
+ * This class is for those who want to instantiate their own {@link WebView},
+ * configure it manually, embed it into own <em>JavaFX</em>
+ * application and based on other events in the application
+ * {@link #runInBrowser(javafx.scene.web.WebView, java.lang.Runnable) re-execute code} 
+ * inside of such {@link WebView}s.
+ * <p>
+ * In case such detailed control is not necessary,
+ * consider using {@link BrowserBuilder}. Btw. when using the {@link BrowserBuilder}
+ * one can execute presenter in headless mode. Just specify: <code>
+ * {@link System}.{@link System#setProperty(java.lang.String, java.lang.String) setProperty}("fxpresenter.headless", "true");
+ * </code>
+ *
+ * 
+ * @author Jaroslav Tulach
+ * @since 0.6
+ */
+public final class FXBrowsers {
+    private FXBrowsers() {
+    }
+    
+    /** Enables the Java/JavaScript bridge (that supports {@link JavaScriptBody} and co.)
+     * in the provided <code>webView</code>. This method returns 
+     * immediately. Once the support is active, it calls back specified
+     * method in <code>onPageLoad</code> class - the class can possibly be
+     * loaded by a different classloader (to enable replacement of
+     * methods with {@link JavaScriptBody} annotations with executable
+     * versions). The method <code>methodName</code> needs to be <code>public</code>
+     * (in a public class), <code>static</code> and take either no parameters
+     * or an array of {@link String}s.
+     * <p>
+     * This method sets {@link WebView#getUserData()} and {@link #runInBrowser(javafx.scene.web.WebView, java.lang.Runnable)}
+     * relies on the value. Please don't alter it.
+     * <p>
+     * Since introduction of {@link Id technology identifiers} the 
+     * provided <code>args</code> strings are also passed to the 
+     * {@link BrwsrCtx context} when it is being 
+     * {@link Contexts#newBuilder(java.lang.Object...) created}
+     * and can influence the selection
+     * of available technologies 
+     * (like {@link org.netbeans.html.json.spi.Technology},
+     * {@link org.netbeans.html.json.spi.Transfer} or
+     * {@link org.netbeans.html.json.spi.WSTransfer}).
+     * 
+     * @param webView the instance of Web View to tweak
+     * @param url the URL of the HTML page to load into the view
+     * @param onPageLoad callback class with method <code>methodName</code>
+     * @param methodName the method to call when the page is loaded
+     * @param args arguments to pass to the <code>methodName</code> method
+     */
+    public static void load(
+        final WebView webView, final URL url, 
+        Class<?> onPageLoad, String methodName,
+        String... args
+    ) {
+        Object[] context = new Object[args.length + 1];
+        System.arraycopy(args, 0, context, 1, args.length);
+        final Load load = new Load(webView, null);
+        context[0] = load;
+        BrowserBuilder.newBrowser(context).
+            loadPage(url.toExternalForm()).
+            loadFinished(load).
+            loadClass(onPageLoad).
+            invoke(methodName, args).
+            showAndWait();
+    }
+    
+    /** Enables the Java/JavaScript bridge (that supports {@link JavaScriptBody} and co.)
+     * in the provided <code>webView</code>. This method returns 
+     * immediately. Once the support is active, it calls back specified
+     * method in <code>onPageLoad</code>'s run method. 
+     * This is more convenient way to initialize the webview, 
+     * but it requires one to make sure
+     * all {@link JavaScriptBody} methods has been post-processed during
+     * compilation and there will be no need to instantiate new classloader.
+     * <p>
+     * This method sets {@link WebView#getUserData()} and {@link #runInBrowser(javafx.scene.web.WebView, java.lang.Runnable)}
+     * relies on the value. Please don't alter it.
+     * 
+     * @param webView the instance of Web View to tweak
+     * @param url the URL of the HTML page to load into the view
+     * @param onPageLoad callback to call when the page is ready
+     * @since 0.8.1
+     */
+    public static void load(
+        WebView webView, final URL url, Runnable onPageLoad
+    ) {
+        load(webView, url, onPageLoad, null);
+    }
+    
+    /** Enables the Java/JavaScript bridge (that supports {@link JavaScriptBody} and co.)
+     * in the provided <code>webView</code>. This method returns 
+     * immediately. Once the support is active, it calls back {@link Runnable#run() run}
+     * method in <code>onPageLoad</code>. 
+     * This is more convenient way to initialize the webview, 
+     * but it requires one to make sure
+     * all {@link JavaScriptBody} methods has been post-processed during
+     * compilation and there will be no need to instantiate new classloader.
+     * <p>
+     * This method sets {@link WebView#getUserData()} and {@link #runInBrowser(javafx.scene.web.WebView, java.lang.Runnable)}
+     * relies on the value. Please don't alter it.
+     * 
+     * @param webView the instance of Web View to tweak
+     * @param url the URL of the HTML page to load into the view
+     * @param onPageLoad callback to call when the page is ready
+     * @param loader the loader to use when constructing initial {@link BrwsrCtx} or <code>null</code>
+     * @since 0.9
+     */
+    public static void load(
+        WebView webView, final URL url, Runnable onPageLoad, ClassLoader loader
+    ) {
+        load(webView, url, onPageLoad, loader, new Object[0]);
+    }
+    
+    /** Enables the Java/JavaScript bridge (that supports {@link JavaScriptBody} and co.)
+     * in the provided <code>webView</code>. This method returns 
+     * immediately. Once the support is active, it calls back {@link Runnable#run() run}
+     * method in <code>onPageLoad</code>. 
+     * This is more convenient way to initialize the webview, 
+     * but it requires one to make sure
+     * all {@link JavaScriptBody} methods has been post-processed during
+     * compilation and there will be no need to instantiate new classloader.
+     * <p>
+     * This method sets {@link WebView#getUserData()} and {@link #runInBrowser(javafx.scene.web.WebView, java.lang.Runnable)}
+     * relies on the value. Please don't alter it.
+     * 
+     * @param webView the instance of Web View to tweak
+     * @param url the URL of the HTML page to load into the view
+     * @param onPageLoad callback to call when the page is ready
+     * @param loader the loader to use when constructing initial {@link BrwsrCtx} or <code>null</code>
+     * @param context additonal configuration to pass to {@link BrowserBuilder#newBrowser(java.lang.Object...)}
+     *   and {@link Contexts#newBuilder(java.lang.Object...)} factory methods 
+     * @since 1.1
+     */
+    public static void load(
+        WebView webView, final URL url, Runnable onPageLoad, ClassLoader loader,
+        Object... context
+    ) {
+        Object[] newCtx = new Object[context.length + 1];
+        System.arraycopy(context, 0, newCtx, 1, context.length);
+        final Load load = new Load(webView, onPageLoad);
+        newCtx[0] = load;
+        BrowserBuilder.newBrowser(newCtx).
+                loadPage(url.toExternalForm()).
+                loadFinished(load).
+                classloader(loader).
+                showAndWait();
+    }
+    
+    /** Executes a code inside of provided {@link WebView}. This method
+     * associates the {@link BrwsrCtx execution context} with provided browser,
+     * so the {@link JavaScriptBody} annotations know where to execute
+     * their JavaScript bodies.
+     * The code is going to be executed synchronously
+     * in case {@link Platform#isFxApplicationThread()} returns <code>true</code>.
+     * Otherwise this method returns immediately and the code is executed
+     * later via {@link Platform#runLater(java.lang.Runnable)}.
+     * <p>
+     * This method relies on {@link WebView#getUserData()} being properly
+     * provided by the <code>load</code> methods in this class.
+     * 
+     * @param webView the web view previously prepared by one of the <code>load</code>
+     *   methods in this class
+     * @param code the code to execute
+     * @throws IllegalArgumentException if the web view was not properly
+     *   initialized
+     * @see BrwsrCtx#execute(java.lang.Runnable) 
+     * @since 0.8.1
+     */
+    public static void runInBrowser(WebView webView, Runnable code) {
+        Object ud = webView.getUserData();
+        if (!(ud instanceof Load)) {
+            throw new IllegalArgumentException();
+        }
+        ((Load)ud).ctx.execute(code);
+    }
+    
+    private static class Load extends AbstractFXPresenter implements Runnable {
+        private final WebView webView;
+        private final Runnable myLoad;
+        private BrwsrCtx ctx;
+
+        public Load(WebView webView, Runnable onLoad) {
+            this.webView = webView;
+            this.myLoad = onLoad;
+            webView.setUserData(this);
+        }
+
+        public void run() {
+            ctx = BrwsrCtx.findDefault(Load.class);
+            if (myLoad != null) {
+                myLoad.run();
+            }
+        }
+        
+        @Override
+        protected void waitFinished() {
+            // don't wait
+        }
+
+        @Override
+        protected WebView findView(final URL resource) {
+            final Worker<Void> w = webView.getEngine().getLoadWorker();
+            w.stateProperty().addListener(new ChangeListener<Worker.State>() {
+                private String previous;
+
+                @Override
+                public void changed(ObservableValue<? extends Worker.State> ov, Worker.State t, Worker.State newState) {
+                    if (newState.equals(Worker.State.SUCCEEDED)) {
+                        if (checkValid()) {
+                            onPageLoad();
+                        }
+                    }
+                    if (newState.equals(Worker.State.FAILED)) {
+                        checkValid();
+                        throw new IllegalStateException("Failed to load " + resource);
+                    }
+                }
+
+                private boolean checkValid() {
+                    final String crnt = webView.getEngine().getLocation();
+                    if (previous != null && !previous.equals(crnt)) {
+                        w.stateProperty().removeListener(this);
+                        return false;
+                    }
+                    previous = crnt;
+                    return true;
+                }
+            });
+
+            return webView;
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/java/net/java/html/boot/fx/package.html
----------------------------------------------------------------------
diff --git a/boot-fx/src/main/java/net/java/html/boot/fx/package.html b/boot-fx/src/main/java/net/java/html/boot/fx/package.html
new file mode 100644
index 0000000..d2ed2c4
--- /dev/null
+++ b/boot-fx/src/main/java/net/java/html/boot/fx/package.html
@@ -0,0 +1,51 @@
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<body>
+    <p>
+        Implementation of {@link net.java.html.boot.BrowserBuilder} that renders
+        using JavaFX WebView and additional {@link net.java.html.boot.fx.FXBrowsers utilities} 
+        to configure individual WebViews.
+    </p>
+</body>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/java/org/netbeans/html/boot/fx/AbstractFXPresenter.java
----------------------------------------------------------------------
diff --git a/boot-fx/src/main/java/org/netbeans/html/boot/fx/AbstractFXPresenter.java b/boot-fx/src/main/java/org/netbeans/html/boot/fx/AbstractFXPresenter.java
new file mode 100644
index 0000000..9164140
--- /dev/null
+++ b/boot-fx/src/main/java/org/netbeans/html/boot/fx/AbstractFXPresenter.java
@@ -0,0 +1,433 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.fx;
+
+import java.io.BufferedReader;
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.Reader;
+import java.lang.ref.WeakReference;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javafx.application.Platform;
+import javafx.scene.Parent;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.web.WebEngine;
+import javafx.scene.web.WebView;
+import netscape.javascript.JSObject;
+import org.netbeans.html.boot.spi.Fn;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public abstract class AbstractFXPresenter implements Fn.Presenter,
+Fn.KeepAlive, Fn.ToJavaScript, Fn.FromJavaScript, Executor, Cloneable {
+    static final Logger LOG = Logger.getLogger(FXPresenter.class.getName());
+    protected static int cnt;
+    protected Runnable onLoad;
+    protected WebEngine engine;
+
+    // transient - e.g. not cloneable
+    private JSObject arraySize;
+    private JSObject wrapArrImpl;
+    private Object undefined;
+
+    @Override
+    protected AbstractFXPresenter clone() {
+        try {
+            AbstractFXPresenter p = (AbstractFXPresenter) super.clone();
+            p.arraySize = null;
+            p.wrapArrImpl = null;
+            p.undefined = null;
+            return p;
+        } catch (CloneNotSupportedException ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+
+    @Override
+    public Fn defineFn(String code, String... names) {
+        return defineJSFn(code, names, null);
+    }
+
+    @Override
+    public Fn defineFn(String code, String[] names, boolean[] keepAlive) {
+        return defineJSFn(code, names, keepAlive);
+    }
+
+
+
+    final JSFn defineJSFn(String code, String[] names, boolean[] keepAlive) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("(function() {\n");
+        sb.append("  return function(\n    ");
+        String sep = "";
+        if (names != null) for (String n : names) {
+            sb.append(sep).append(n);
+            sep = ",";
+        }
+        sb.append("  \n) {\n");
+        sb.append(code);
+        sb.append("};\n");
+        sb.append("})();\n");
+        if (LOG.isLoggable(Level.FINE)) {
+            LOG.log(Level.FINE,
+                "defining function #{0}:\n{1}\n",
+                new Object[] { ++cnt, code }
+            );
+        }
+        JSObject x = (JSObject) engine.executeScript(sb.toString());
+        return new JSFn(this, x, cnt, keepAlive);
+    }
+
+    @Override
+    public void loadScript(Reader code) throws Exception {
+        BufferedReader r = new BufferedReader(code);
+        StringBuilder sb = new StringBuilder();
+        for (;;) {
+            String l = r.readLine();
+            if (l == null) {
+                break;
+            }
+            sb.append(l).append('\n');
+        }
+        final String script = sb.toString();
+        engine.executeScript(script);
+    }
+
+    protected final void onPageLoad() {
+        Closeable c = Fn.activate(this.clone());
+        try {
+            onLoad.run();
+        } finally {
+            try {
+                c.close();
+            } catch (IOException ex) {
+                LOG.log(Level.SEVERE, null, ex);
+            }
+        }
+    }
+
+    @Override
+    public void displayPage(final URL resource, final Runnable onLoad) {
+        this.onLoad = onLoad;
+        final WebView view = findView(resource);
+        this.engine = view.getEngine();
+        boolean inspectOn = false;
+        try {
+            if (FXInspect.initialize(engine)) {
+                inspectOn = true;
+            }
+        } catch (Throwable ex) {
+            ex.printStackTrace();
+        }
+        final boolean isFirebugOn = Boolean.getBoolean("firebug.lite"); // NOI18N
+        final boolean isInspectOn = inspectOn;
+        class Run implements Runnable {
+
+            @Override
+            public void run() {
+                if (isInspectOn || isFirebugOn) {
+                    view.setContextMenuEnabled(true);
+                    final Parent p = view.getParent();
+                    if (p instanceof BorderPane) {
+                        BorderPane bp = (BorderPane) p;
+                        if (bp.getTop() == null) {
+                            bp.setTop(new FXToolbar(view, bp, isFirebugOn));
+                        }
+                    }
+                }
+                engine.load(resource.toExternalForm());
+            }
+        }
+        Run run = new Run();
+        if (Platform.isFxApplicationThread()) {
+            run.run();
+        } else {
+            Platform.runLater(run);
+        }
+        waitFinished();
+    }
+
+    protected abstract void waitFinished();
+
+    protected abstract WebView findView(final URL resource);
+
+    final JSObject convertArrays(Object[] arr) {
+        for (int i = 0; i < arr.length; i++) {
+            if (arr[i] instanceof Object[]) {
+                arr[i] = convertArrays((Object[]) arr[i]);
+            }
+        }
+        final JSObject wrapArr = (JSObject)wrapArrFn().call("array", arr); // NOI18N
+        return wrapArr;
+    }
+
+    private final JSObject wrapArrFn() {
+        if (wrapArrImpl == null) {
+            try {
+                wrapArrImpl = (JSObject)defineJSFn("  var k = {};"
+                    + "  k.array= function() {"
+                    + "    return Array.prototype.slice.call(arguments);"
+                    + "  };"
+                    + "  return k;", null, null
+                ).invokeImpl(null, false);
+            } catch (Exception ex) {
+                throw new IllegalStateException(ex);
+            }
+        }
+        return wrapArrImpl;
+    }
+
+    final Object undefined() {
+        if (undefined == null) {
+            undefined = engine.executeScript("undefined");
+        }
+        return undefined;
+    }
+
+    final Object checkArray(Object val) {
+        if (!(val instanceof JSObject)) {
+            return val;
+        }
+        int length = ((Number) arraySizeFn().call("array", val, null)).intValue();
+        if (length == -1) {
+            return val;
+        }
+        Object[] arr = new Object[length];
+        arraySizeFn().call("array", val, arr);
+        clearUndefinedArray(arr);
+        return arr;
+    }
+
+    private void clearUndefinedArray(Object[] arr) {
+        for (int i = 0; i < arr.length; i++) {
+            if (arr[i] == undefined) {
+                arr[i] = null;
+                continue;
+            }
+            if (arr[i] instanceof Object[]) {
+                clearUndefinedArray((Object[])arr[i]);
+            }
+        }
+    }
+
+    private final JSObject arraySizeFn() {
+        if (arraySize == null) {
+            try {
+                arraySize = (JSObject)defineJSFn("  var k = {};"
+                    + "  k.array = function(arr, to) {"
+                    + "    if (to === null) {"
+                    + "      if (Object.prototype.toString.call(arr) === '[object Array]') return arr.length;"
+                    + "      else return -1;"
+                    + "    } else {"
+                    + "      var l = arr.length;"
+                    + "      for (var i = 0; i < l; i++) to[i] = arr[i];"
+                    + "      return l;"
+                    + "    }"
+                    + "  };"
+                    + "  return k;", null, null
+                ).invokeImpl(null, false);
+            } catch (Exception ex) {
+                throw new IllegalStateException(ex);
+            }
+        }
+        return arraySize;
+    }
+
+    @Override
+    public Object toJava(Object toJS) {
+        if (toJS instanceof Weak) {
+            toJS = ((Weak)toJS).get();
+        }
+        if (toJS == undefined()) {
+            return null;
+        }
+        return checkArray(toJS);
+    }
+
+    @Override
+    public Object toJavaScript(Object toReturn) {
+        if (toReturn instanceof Object[]) {
+            return convertArrays((Object[])toReturn);
+        } else {
+            if (toReturn instanceof Character) {
+                return (int)(Character)toReturn;
+            }
+            return toReturn;
+        }
+    }
+
+    @Override public void execute(final Runnable r) {
+        if (Platform.isFxApplicationThread()) {
+            Closeable c = Fn.activate(this);
+            try {
+                r.run();
+            } finally {
+                try {
+                    c.close();
+                } catch (IOException ex) {
+                    // ignore
+                }
+            }
+        } else {
+            class Wrap implements Runnable {
+                @Override
+                public void run() {
+                    Closeable c = Fn.activate(AbstractFXPresenter.this);
+                    try {
+                        r.run();
+                    } finally {
+                        try {
+                            c.close();
+                        } catch (IOException ex) {
+                            // ignore
+                        }
+                    }
+                }
+            }
+            Platform.runLater(new Wrap());
+        }
+    }
+
+    private static final class JSFn extends Fn {
+
+        private final JSObject fn;
+        private static int call;
+        private final int id;
+        private final boolean[] keepAlive;
+
+        public JSFn(AbstractFXPresenter p, JSObject fn, int id, boolean[] keepAlive) {
+            super(p);
+            this.fn = fn;
+            this.id = id;
+            this.keepAlive = keepAlive;
+        }
+
+        @Override
+        public Object invoke(Object thiz, Object... args) throws Exception {
+            return invokeImpl(thiz, true, args);
+        }
+
+        final Object invokeImpl(Object thiz, boolean arrayChecks, Object... args) throws Exception {
+            try {
+                final AbstractFXPresenter presenter = (AbstractFXPresenter) presenter();
+                if (LOG.isLoggable(Level.FINE)) {
+                    LOG.log(Level.FINE, "calling {0} function #{1}", new Object[]{++call, id});
+                    LOG.log(Level.FINER, "  thiz  : {0}", thiz);
+                    LOG.log(Level.FINER, "  params: {0}", Arrays.asList(args));
+                }
+                List<Object> all = new ArrayList<Object>(args.length + 1);
+                all.add(thiz == null ? fn : thiz);
+                for (int i = 0; i < args.length; i++) {
+                    Object conv = args[i];
+                    if (arrayChecks) {
+                        if (args[i] instanceof Object[]) {
+                            Object[] arr = (Object[]) args[i];
+                            conv = presenter.convertArrays(arr);
+                        }
+                        if (conv != null && keepAlive != null &&
+                            !keepAlive[i] && !isJSReady(conv) &&
+                            !conv.getClass().getSimpleName().equals("$JsCallbacks$") // NOI18N
+                        ) {
+                            conv = new Weak(conv);
+                        }
+                        if (conv instanceof Character) {
+                            conv = (int)(Character)conv;
+                        }
+                    }
+                    all.add(conv);
+                }
+                Object ret = fn.call("call", all.toArray()); // NOI18N
+                if (ret instanceof Weak) {
+                    ret = ((Weak)ret).get();
+                }
+                if (ret == fn || ret == presenter.undefined()) {
+                    return null;
+                }
+                if (!arrayChecks) {
+                    return ret;
+                }
+                return presenter.checkArray(ret);
+            } catch (Error t) {
+                t.printStackTrace();
+                throw t;
+            } catch (Exception t) {
+                t.printStackTrace();
+                throw t;
+            }
+        }
+    }
+
+    private static boolean isJSReady(Object obj) {
+        if (obj == null) {
+            return true;
+        }
+        if (obj instanceof String) {
+            return true;
+        }
+        if (obj instanceof Number) {
+            return true;
+        }
+        if (obj instanceof JSObject) {
+            return true;
+        }
+        if (obj instanceof Character) {
+            return true;
+        }
+        return false;
+    }
+
+    private static final class Weak extends WeakReference<Object> {
+        public Weak(Object referent) {
+            super(referent);
+            assert !(referent instanceof Weak);
+        }
+    } // end of Weak
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/java/org/netbeans/html/boot/fx/Dbgr.java
----------------------------------------------------------------------
diff --git a/boot-fx/src/main/java/org/netbeans/html/boot/fx/Dbgr.java b/boot-fx/src/main/java/org/netbeans/html/boot/fx/Dbgr.java
new file mode 100644
index 0000000..0d2b325
--- /dev/null
+++ b/boot-fx/src/main/java/org/netbeans/html/boot/fx/Dbgr.java
@@ -0,0 +1,87 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.fx;
+
+import java.lang.reflect.Method;
+import java.util.logging.Level;
+import javafx.scene.web.WebEngine;
+import javafx.util.Callback;
+import static org.netbeans.html.boot.fx.AbstractFXPresenter.LOG;
+
+/** Debugger bridge to shield us from propriatory impl APIs.
+ *
+ * @author Jaroslav Tulach
+ */
+final class Dbgr {
+    final Object dbg;
+    final Method sendMsg;
+    
+    Dbgr(WebEngine eng, Callback<String,Void> callback) {
+        Object d;
+        Method m;
+        try {
+            d = eng.getClass().getMethod("impl_getDebugger").invoke(eng); // NOI18N
+            Class<?> debugger = eng.getClass().getClassLoader().loadClass("com.sun.javafx.scene.web.Debugger"); // NOI18N
+            debugger.getMethod("setEnabled", boolean.class).invoke(d, true); // NOI18N
+            debugger.getMethod("setMessageCallback", Callback.class).invoke(d, callback); // NOI18N
+            m = debugger.getMethod("sendMessage", String.class); // NOI18N
+        } catch (Exception ex) {
+            LOG.log(Level.INFO, null, ex);
+            d = null;
+            m = null;
+        }
+        dbg = d;
+        sendMsg = m;
+    }
+
+    void sendMessage(String msg) {
+        try {
+            if (dbg != null) {
+                sendMsg.invoke(dbg, msg);
+            }
+        } catch (Exception ex) {
+            LOG.log(Level.INFO, null, ex);
+        }
+    }
+    
+}


[23/24] incubator-netbeans-html4j git commit: [INFRA-15006] Initial donation of HTML/Java NetBeans API. Equivalent to the content of html4j-donation-review.zip donated as part of ApacheNetBeansDonation1.zip with SHA256 being 7f2ca0f61953a190613c9a0fbcc1b

Posted by jt...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXBrwsr.java
----------------------------------------------------------------------
diff --git a/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXBrwsr.java b/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXBrwsr.java
new file mode 100644
index 0000000..71c8547
--- /dev/null
+++ b/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXBrwsr.java
@@ -0,0 +1,427 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.fx;
+
+import java.net.URL;
+import java.util.ResourceBundle;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.prefs.Preferences;
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.concurrent.Worker;
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.geometry.Insets;
+import javafx.geometry.Pos;
+import javafx.geometry.Rectangle2D;
+import javafx.scene.Scene;
+import javafx.scene.control.Button;
+import javafx.scene.control.TextField;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.VBox;
+import javafx.scene.text.Text;
+import javafx.scene.web.PromptData;
+import javafx.scene.web.WebEvent;
+import javafx.scene.web.WebView;
+import javafx.stage.Modality;
+import javafx.stage.Screen;
+import javafx.stage.Stage;
+import javafx.stage.Window;
+import javafx.stage.WindowEvent;
+import javafx.util.Callback;
+
+/** This is an implementation class, to implement browser builder API. Just
+ * include this JAR on classpath and the browser builder API will find
+ * this implementation automatically.
+ */
+public class FXBrwsr extends Application {
+    private static final Logger LOG = Logger.getLogger(FXBrwsr.class.getName());
+    private static FXBrwsr INSTANCE;
+    private static final CountDownLatch FINISHED = new CountDownLatch(1);
+    private BorderPane root;
+
+    public static synchronized WebView findWebView(final URL url, final FXPresenter onLoad) {
+        if (INSTANCE == null) {
+            final String callee = findCalleeClassName();
+            Executors.newFixedThreadPool(1).submit(new Runnable() {
+                @Override
+                public void run() {
+                    if (!Platform.isFxApplicationThread()) {
+                        try {
+                            Platform.runLater(this);
+                        } catch (IllegalStateException ex) {
+                            try {
+                                FXBrwsr.launch(FXBrwsr.class, callee);
+                            } catch (Throwable t) {
+                                t.printStackTrace();
+                            } finally {
+                                FINISHED.countDown();
+                            }
+                        }
+                    } else {
+                        FXBrwsr brwsr = new FXBrwsr();
+                        brwsr.start(new Stage(), callee);
+                        INSTANCE = brwsr;
+                        FINISHED.countDown();
+                    }
+                }
+            });
+        }
+        while (INSTANCE == null) {
+            try {
+                FXBrwsr.class.wait();
+            } catch (InterruptedException ex) {
+                // wait more
+            }
+        }
+        if (!Platform.isFxApplicationThread()) {
+            final WebView[] arr = {null};
+            final CountDownLatch waitForResult = new CountDownLatch(1);
+            Platform.runLater(new Runnable() {
+                @Override
+                public void run() {
+                    arr[0] = INSTANCE.newView(url, onLoad);
+                    waitForResult.countDown();
+                }
+            });
+            for (;;) {
+                try {
+                    waitForResult.await();
+                    break;
+                } catch (InterruptedException ex) {
+                    LOG.log(Level.INFO, null, ex);
+                }
+            }
+            return arr[0];
+        } else {
+            return INSTANCE.newView(url, onLoad);
+        }
+    }
+
+    static synchronized Stage findStage() throws InterruptedException {
+        while (INSTANCE == null) {
+            FXBrwsr.class.wait();
+        }
+        return INSTANCE.stage;
+    }
+
+    private Stage stage;
+
+    @Override
+    public void start(Stage primaryStage) throws Exception {
+        start(primaryStage, this.getParameters().getRaw().get(0));
+    }
+
+    final void start(Stage primaryStage, String callee) {
+        BorderPane r = new BorderPane();
+        Object[] arr = findInitialSize(callee);
+        Scene scene = new Scene(r, (Double)arr[2], (Double)arr[3]);
+        primaryStage.setScene(scene);
+        this.root = r;
+        this.stage = primaryStage;
+        synchronized (FXBrwsr.class) {
+            INSTANCE = this;
+            FXBrwsr.class.notifyAll();
+        }
+        primaryStage.setX((Double)arr[0]);
+        primaryStage.setY((Double)arr[1]);
+        if (arr[4] != null) {
+            scene.getWindow().setOnCloseRequest((EventHandler<WindowEvent>) arr[4]);
+        }
+        if (Boolean.getBoolean("fxpresenter.headless")) {
+            return;
+        }
+        primaryStage.show();
+    }
+
+    static String findCalleeClassName() {
+        StackTraceElement[] frames = new Exception().getStackTrace();
+        for (StackTraceElement e : frames) {
+            String cn = e.getClassName();
+            if (cn.startsWith("org.netbeans.html.")) { // NOI18N
+                continue;
+            }
+            if (cn.startsWith("net.java.html.")) { // NOI18N
+                continue;
+            }
+            if (cn.startsWith("java.")) { // NOI18N
+                continue;
+            }
+            if (cn.startsWith("javafx.")) { // NOI18N
+                continue;
+            }
+            if (cn.startsWith("com.sun.")) { // NOI18N
+                continue;
+            }
+            return cn;
+        }
+        return "org.netbeans.html"; // NOI18N
+    }
+
+    private static Object[] findInitialSize(String callee) {
+        final Preferences prefs = Preferences.userRoot().node(callee.replace('.', '/'));
+        Rectangle2D screen = Screen.getPrimary().getBounds();
+        double x = prefs.getDouble("x", screen.getWidth() * 0.05); // NOI18N
+        double y = prefs.getDouble("y", screen.getHeight() * 0.05); // NOI18N
+        double width = prefs.getDouble("width", screen.getWidth() * 0.9); // NOI18N
+        double height = prefs.getDouble("height", screen.getHeight() * 0.9); // NOI18N
+
+        Object[] arr = {
+            x, y, width, height, null
+        };
+
+        if (!callee.equals("org.netbeans.html")) { // NOI18N
+            arr[4] = new EventHandler<WindowEvent>() {
+                @Override
+                public void handle(WindowEvent event) {
+                    Window window = (Window) event.getSource();
+                    prefs.putDouble("x", window.getX()); // NOI18N
+                    prefs.putDouble("y", window.getY()); // NOI18N
+                    prefs.putDouble("width", window.getWidth()); // NOI18N
+                    prefs.putDouble("height", window.getHeight()); // NOI18N
+                }
+            };
+        }
+
+        return arr;
+    }
+
+    private WebView newView(final URL url, final FXPresenter onLoad) {
+        final WebView view = new WebView();
+        view.setContextMenuEnabled(false);
+        Stage newStage;
+        BorderPane bp;
+        if (root == null) {
+            newStage = new Stage();
+            newStage.initOwner(stage);
+            bp = new BorderPane();
+            newStage.setScene(new Scene(bp));
+            newStage.show();
+        } else {
+            bp = root;
+            newStage = stage;
+            root = null;
+        }
+
+        attachHandlers(view, newStage);
+        bp.setCenter(view);
+        final Worker<Void> w = view.getEngine().getLoadWorker();
+        w.stateProperty().addListener(new ChangeListener<Worker.State>() {
+            private String previous;
+
+            @Override
+            public void changed(ObservableValue<? extends Worker.State> ov, Worker.State t, Worker.State newState) {
+                if (newState.equals(Worker.State.SUCCEEDED)) {
+                    if (checkValid()) {
+                        FXConsole.register(view.getEngine());
+                        onLoad.onPageLoad();
+                    }
+                }
+                if (newState.equals(Worker.State.FAILED)) {
+                    throw new IllegalStateException("Failed to load " + url);
+                }
+            }
+            private boolean checkValid() {
+                final String crnt = view.getEngine().getLocation();
+                if (previous != null && !previous.equals(crnt)) {
+                    w.stateProperty().removeListener(this);
+                    return false;
+                }
+                previous = crnt;
+                return true;
+            }
+
+        });
+        class Title implements ChangeListener<String> {
+
+            private String title;
+
+            public Title() {
+                super();
+            }
+
+            @Override
+            public void changed(ObservableValue<? extends String> ov, String t, String t1) {
+                title = view.getEngine().getTitle();
+                if (title != null) {
+                    stage.setTitle(title);
+                }
+            }
+        }
+        final Title x = new Title();
+        view.getEngine().titleProperty().addListener(x);
+        x.changed(null, null, null);
+        return view;
+    }
+
+    private static void attachHandlers(final WebView view, final Stage owner) {
+        view.getEngine().setOnAlert(new EventHandler<WebEvent<String>>() {
+            @Override
+            public void handle(WebEvent<String> t) {
+                final Stage dialogStage = new Stage();
+                dialogStage.initModality(Modality.WINDOW_MODAL);
+                dialogStage.initOwner(owner);
+                ResourceBundle r = ResourceBundle.getBundle("org/netbeans/html/boot/fx/Bundle"); // NOI18N
+                dialogStage.setTitle(r.getString("AlertTitle")); // NOI18N
+                final Button button = new Button(r.getString("AlertCloseButton")); // NOI18N
+                final Text text = new Text(t.getData());
+                VBox box = new VBox();
+                box.setAlignment(Pos.CENTER);
+                box.setSpacing(10);
+                box.setPadding(new Insets(10));
+                box.getChildren().addAll(text, button);
+                dialogStage.setScene(new Scene(box));
+                button.setCancelButton(true);
+                button.setOnAction(new CloseDialogHandler(dialogStage, null));
+                dialogStage.centerOnScreen();
+                dialogStage.showAndWait();
+            }
+        });
+        view.getEngine().setConfirmHandler(new Callback<String, Boolean>() {
+            @Override
+            public Boolean call(String question) {
+                final Stage dialogStage = new Stage();
+                dialogStage.initModality(Modality.WINDOW_MODAL);
+                dialogStage.initOwner(owner);
+                ResourceBundle r = ResourceBundle.getBundle("org/netbeans/html/boot/fx/Bundle"); // NOI18N
+                dialogStage.setTitle(r.getString("ConfirmTitle")); // NOI18N
+                final Button ok = new Button(r.getString("ConfirmOKButton")); // NOI18N
+                final Button cancel = new Button(r.getString("ConfirmCancelButton")); // NOI18N
+                final Text text = new Text(question);
+                final Insets ins = new Insets(10);
+                final VBox box = new VBox();
+                box.setAlignment(Pos.CENTER);
+                box.setSpacing(10);
+                box.setPadding(ins);
+                final HBox buttons = new HBox(10);
+                buttons.getChildren().addAll(ok, cancel);
+                buttons.setAlignment(Pos.CENTER);
+                buttons.setPadding(ins);
+                box.getChildren().addAll(text, buttons);
+                dialogStage.setScene(new Scene(box));
+                ok.setCancelButton(false);
+
+                final boolean[] res = new boolean[1];
+                ok.setOnAction(new CloseDialogHandler(dialogStage, res));
+                cancel.setCancelButton(true);
+                cancel.setOnAction(new CloseDialogHandler(dialogStage, null));
+                dialogStage.centerOnScreen();
+                dialogStage.showAndWait();
+                return res[0];
+            }
+        });
+        view.getEngine().setPromptHandler(new Callback<PromptData, String>() {
+            @Override
+            public String call(PromptData prompt) {
+                final Stage dialogStage = new Stage();
+                dialogStage.initModality(Modality.WINDOW_MODAL);
+                dialogStage.initOwner(owner);
+                ResourceBundle r = ResourceBundle.getBundle("org/netbeans/html/boot/fx/Bundle"); // NOI18N
+                dialogStage.setTitle(r.getString("PromptTitle")); // NOI18N
+                final Button ok = new Button(r.getString("PromptOKButton")); // NOI18N
+                final Button cancel = new Button(r.getString("PromptCancelButton")); // NOI18N
+                final Text text = new Text(prompt.getMessage());
+                final TextField line = new TextField();
+                if (prompt.getDefaultValue() != null) {
+                    line.setText(prompt.getDefaultValue());
+                }
+                final Insets ins = new Insets(10);
+                final VBox box = new VBox();
+                box.setAlignment(Pos.CENTER);
+                box.setSpacing(10);
+                box.setPadding(ins);
+                final HBox buttons = new HBox(10);
+                buttons.getChildren().addAll(ok, cancel);
+                buttons.setAlignment(Pos.CENTER);
+                buttons.setPadding(ins);
+                box.getChildren().addAll(text, line, buttons);
+                dialogStage.setScene(new Scene(box));
+                ok.setCancelButton(false);
+
+                final boolean[] res = new boolean[1];
+                ok.setOnAction(new CloseDialogHandler(dialogStage, res));
+                cancel.setCancelButton(true);
+                cancel.setOnAction(new CloseDialogHandler(dialogStage, null));
+                dialogStage.centerOnScreen();
+                dialogStage.showAndWait();
+                return res[0] ? line.getText() : null;
+            }
+        });
+    }
+
+    static void waitFinished() {
+        for (;;) {
+            try {
+                FINISHED.await();
+                break;
+            } catch (InterruptedException ex) {
+                LOG.log(Level.INFO, null, ex);
+            }
+        }
+    }
+
+    private static final class CloseDialogHandler implements EventHandler<ActionEvent> {
+        private final Stage dialogStage;
+        private final boolean[] res;
+
+        public CloseDialogHandler(Stage dialogStage, boolean[] res) {
+            this.dialogStage = dialogStage;
+            this.res = res;
+        }
+
+        @Override
+        public void handle(ActionEvent t) {
+            dialogStage.close();
+            if (res != null) {
+                res[0] = true;
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXConsole.java
----------------------------------------------------------------------
diff --git a/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXConsole.java b/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXConsole.java
new file mode 100644
index 0000000..d176ede
--- /dev/null
+++ b/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXConsole.java
@@ -0,0 +1,84 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.fx;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javafx.scene.web.WebEngine;
+import netscape.javascript.JSObject;
+
+/** This is an implementation package - just
+ * include its JAR on classpath and use official browser builder API
+ * to access the functionality.
+ * <p>
+ * Redirects JavaScript's messages to Java's {@link Logger}.
+ *
+ * @author Jaroslav Tulach
+ */
+public final class FXConsole {
+    static final Logger LOG = Logger.getLogger(FXConsole.class.getName());
+    
+    private FXConsole() {
+    }
+
+    static void register(WebEngine eng) {
+        JSObject fn = (JSObject) eng.executeScript(""
+            + "(function(attr, l, c) {"
+            + "  window.console[attr] = function(msg) { c.log(l, msg); };"
+            + "})"
+        );
+        FXConsole c = new FXConsole();
+        c.registerImpl(fn, "log", Level.INFO);
+        c.registerImpl(fn, "info", Level.INFO);
+        c.registerImpl(fn, "warn", Level.WARNING);
+        c.registerImpl(fn, "error", Level.SEVERE);
+    }
+    
+    private void registerImpl(JSObject eng, String attr, Level l) {
+        eng.call("call", null, attr, l, this);
+    }
+    
+    public void log(Level l, String msg) {
+        LOG.log(l, msg);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXInspect.java
----------------------------------------------------------------------
diff --git a/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXInspect.java b/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXInspect.java
new file mode 100644
index 0000000..2c8bc4f
--- /dev/null
+++ b/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXInspect.java
@@ -0,0 +1,134 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.fx;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.nio.charset.StandardCharsets;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javafx.application.Platform;
+import javafx.scene.web.WebEngine;
+import javafx.util.Callback;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+final class FXInspect implements Runnable {
+    static final Logger LOG = Logger.getLogger(FXInspect.class.getName());
+    
+    
+    private final WebEngine engine;
+    private final ObjectInputStream input;
+    private Dbgr dbg;
+    
+    private FXInspect(WebEngine engine, int port) throws IOException {
+        this.engine = engine;
+        
+        Socket socket = new Socket(InetAddress.getByName(null), port);
+        ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
+        this.input = new ObjectInputStream(socket.getInputStream());
+        initializeDebugger(output);
+    }
+    
+    static boolean initialize(WebEngine engine) {
+        final int inspectPort = Integer.getInteger("netbeans.inspect.port", -1); // NOI18N
+        if (inspectPort != -1) {
+            try {
+                FXInspect inspector = new FXInspect(engine, inspectPort);
+                Thread t = new Thread(inspector, "FX<->NetBeans Inspector");
+                t.start();
+                return true;
+            } catch (IOException ex) {
+                LOG.log(Level.INFO, "Cannot connect to NetBeans IDE to port " + inspectPort, ex); // NOI18N
+            }
+        }
+        return false;
+    }
+    
+    private void initializeDebugger(final ObjectOutputStream output) {
+        Platform.runLater(new Runnable() {
+            @Override
+            public void run() {
+                dbg = new Dbgr(engine, new Callback<String,Void>() {
+                    @Override
+                    public Void call(String message) {
+                        try {
+                            byte[] bytes = message.getBytes(StandardCharsets.UTF_8);
+                            output.writeInt(bytes.length);
+                            output.write(bytes);
+                            output.flush();
+                        } catch (IOException ioex) {
+                            ioex.printStackTrace();
+                        }
+                        return null;
+                    }
+                });
+            }
+        });
+    }
+
+    @Override
+    public void run() {
+        try {
+            while (true) {
+                int length = input.readInt();
+                byte[] bytes = new byte[length];
+                input.readFully(bytes);
+                final String message = new String(bytes, StandardCharsets.UTF_8);
+                Platform.runLater(new Runnable() {
+                    @Override
+                    public void run() {
+                        dbg.sendMessage(message);
+                    }
+                });
+            }
+        } catch (IOException ex) {
+            LOG.log(Level.WARNING, null, ex);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXPresenter.java
----------------------------------------------------------------------
diff --git a/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXPresenter.java b/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXPresenter.java
new file mode 100644
index 0000000..c54a1a1
--- /dev/null
+++ b/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXPresenter.java
@@ -0,0 +1,88 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.fx;
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+import javafx.scene.web.WebView;
+import net.java.html.boot.BrowserBuilder;
+import org.netbeans.html.boot.spi.Fn;
+import org.openide.util.lookup.ServiceProvider;
+
+/** This is an implementation class, use {@link BrowserBuilder} API. Just
+ * include this JAR on classpath and the {@link BrowserBuilder} API will find
+ * this implementation automatically.
+ *
+ * @author Jaroslav Tulach
+ */
+@ServiceProvider(service = Fn.Presenter.class)
+public final class FXPresenter extends AbstractFXPresenter {
+    static {
+        try {
+            try {
+                Class<?> c = Class.forName("javafx.application.Platform");
+                // OK, on classpath
+            } catch (ClassNotFoundException classNotFoundException) {
+                Method m = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
+                m.setAccessible(true);
+                File f = new File(System.getProperty("java.home"), "lib/jfxrt.jar");
+                if (f.exists()) {
+                    URL l = f.toURI().toURL();
+                    m.invoke(ClassLoader.getSystemClassLoader(), l);
+                }
+            }
+        } catch (Exception ex) {
+            throw new LinkageError("Can't add jfxrt.jar on the classpath", ex);
+        }
+    }
+
+    protected void waitFinished() {
+        FXBrwsr.waitFinished();
+    }
+
+    protected WebView findView(final URL resource) {
+        return FXBrwsr.findWebView(resource, this);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXToolbar.java
----------------------------------------------------------------------
diff --git a/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXToolbar.java b/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXToolbar.java
new file mode 100644
index 0000000..20d4e10
--- /dev/null
+++ b/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXToolbar.java
@@ -0,0 +1,437 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.fx;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.prefs.Preferences;
+import javafx.beans.InvalidationListener;
+import javafx.beans.Observable;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.collections.FXCollections;
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.scene.Group;
+import javafx.scene.Scene;
+import javafx.scene.control.Button;
+import javafx.scene.control.CheckBox;
+import javafx.scene.control.ComboBox;
+import javafx.scene.control.Separator;
+import javafx.scene.control.Toggle;
+import javafx.scene.control.ToggleButton;
+import javafx.scene.control.ToggleGroup;
+import javafx.scene.control.ToolBar;
+import javafx.scene.control.Tooltip;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.web.WebEngine;
+import javafx.scene.web.WebView;
+import javafx.stage.Screen;
+import javafx.stage.Stage;
+import javafx.stage.Window;
+
+final class FXToolbar extends ToolBar {
+    private final ArrayList<ResizeBtn> resizeButtons;
+    private final WebView webView;
+    private final BorderPane container;
+    private final ToggleGroup resizeGroup = new ToggleGroup();
+    private final ComboBox<String> comboZoom = new ComboBox<String>();
+    private WatchDir watcher;
+    
+    FXToolbar(WebView wv, BorderPane container, boolean enableFirebug) {
+        this.webView = wv;
+        this.container = container;
+        
+        List<ResizeOption> options = ResizeOption.loadAll();
+        options.add( 0, ResizeOption.SIZE_TO_FIT );
+        resizeButtons = new ArrayList<ResizeBtn>( options.size() );
+
+        for( ResizeOption ro : options ) {
+            ResizeBtn button = new ResizeBtn(ro);
+            resizeButtons.add( button );
+            resizeGroup.getToggles().add( button );
+            getItems().add( button );
+        }
+        resizeButtons.get( 0 ).setSelected( true );
+        resizeGroup.selectedToggleProperty().addListener( new InvalidationListener() {
+
+            @Override
+            public void invalidated( Observable o ) {
+                resize();
+            }
+        });
+        
+        getItems().add( new Separator() );
+
+        getItems().add( comboZoom );
+        ArrayList<String> zoomModel = new ArrayList<String>( 6 );
+        zoomModel.add( "200%" ); //NOI18N
+        zoomModel.add( "150%" ); //NOI18N
+        zoomModel.add( "100%" ); //NOI18N
+        zoomModel.add( "75%" ); //NOI18N
+        zoomModel.add( "50%" ); //NOI18N
+        comboZoom.setItems( FXCollections.observableList( zoomModel ) );
+        comboZoom.setEditable( true );
+        comboZoom.setValue( "100%" ); //NOI18N
+        comboZoom.valueProperty().addListener( new ChangeListener<String>() {
+
+            @Override
+            public void changed( ObservableValue<? extends String> ov, String t, String t1 ) {
+                String newZoom = zoom( t1 );
+                comboZoom.setValue( newZoom );
+            }
+        });
+        
+        getItems().add(new Separator());
+        final CheckBox automatic = new CheckBox("Automatic");
+        final Preferences prefs = Preferences.userNodeForPackage(FXToolbar.class);
+        final String ar = "automaticReload"; // NOI18N
+        automatic.setSelected(prefs.getBoolean(ar, true));
+        getItems().add(automatic);
+        final Button reload = new Button("Reload");
+        getItems().add(reload);
+        reload.setOnAction(new EventHandler<ActionEvent>() {
+            @Override
+            public void handle(ActionEvent event) {
+                webView.getEngine().reload();
+            }
+        });
+        automatic.setOnAction(new EventHandler<ActionEvent>() {
+            @Override
+            public void handle(ActionEvent event) {
+                prefs.putBoolean(ar, automatic.isSelected());
+                listenOnChanges(automatic.isSelected());
+            }
+        });
+        webView.getEngine().locationProperty().addListener(new ChangeListener<String>() {
+            @Override
+            public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
+                listenOnChanges(automatic.isSelected());
+            }
+        });
+        if (enableFirebug){
+        final Button firebug = new Button("Firebug");
+        getItems().add(firebug);
+        firebug.setOnAction(new EventHandler<ActionEvent>() {
+
+            @Override
+            public void handle(ActionEvent event) {
+                enableFirebug(webView.getEngine());
+                firebug.setDisable(true);
+            }
+        });}
+    }
+
+    private String zoom( String zoomFactor ) {
+        if( zoomFactor.trim().isEmpty() )
+            return null;
+
+        try {
+            zoomFactor = zoomFactor.replaceAll( "\\%", ""); //NOI18N
+            zoomFactor = zoomFactor.trim();
+            double zoom = Double.parseDouble( zoomFactor );
+            zoom = Math.abs( zoom )/100;
+            if( zoom <= 0.0 )
+                return null;
+            webView.setScaleX(zoom);
+            webView.setScaleY(zoom);
+            webView.setScaleZ(zoom);
+            return (int)(100*zoom) + "%"; //NOI18N
+        } catch( NumberFormatException nfe ) {
+            //ignore
+        }
+        return null;
+    }
+
+    private void resize() {
+        Toggle selection = resizeGroup.getSelectedToggle();
+        if( selection instanceof ResizeBtn ) {
+            ResizeOption ro = ((ResizeBtn)selection).getResizeOption();
+            if( ro == ResizeOption.SIZE_TO_FIT ) {
+                _autofit();
+            } else {
+                _resize( ro.getWidth(), ro.getHeight() );
+            }
+        }
+
+    }
+
+    private void _resize(final double width, final double height) {
+        Window window = container.getScene().getWindow();
+        // size difference between root node and window depends on OS and Decorations
+        double diffY = window.getHeight() - container.getHeight();
+        double diffX = window.getWidth() - container.getWidth();
+
+        webView.setMaxWidth(width);
+        webView.setMaxHeight(height);
+        webView.setMinWidth(width);
+        webView.setMinHeight(height);
+        javafx.geometry.Rectangle2D screenBounds = Screen.getPrimary().getBounds();
+        double scaleX = screenBounds.getWidth() / ( width + diffX );
+        double scaleY = screenBounds.getHeight() / ( height + diffY );
+        // calculate scale factor if too big for device, the .1 adds some padding
+        double scale = Math.min(Math.min(scaleX, scaleY), 1.1) - .1;
+        webView.setScaleX(scale);
+        webView.setScaleY(scale);
+        container.getScene().setRoot(new Group());
+        ((Stage)window).setScene(new Scene(container, width * scale, height * scale));
+    }
+
+    private void _autofit() {
+        if (container.getCenter() != webView) {
+            container.setCenter(webView);
+        }
+        webView.setMaxWidth( Integer.MAX_VALUE );
+        webView.setMaxHeight( Integer.MAX_VALUE );
+        webView.setMinWidth( -1 );
+        webView.setMinHeight( -1 );
+        webView.autosize();
+    }
+
+    /**
+     * Button to resize the browser window.
+     * Taken from NetBeans. Kept GPLwithCPEx license.
+     * Portions Copyright 2012 Oracle.
+     *
+     * @author S. Aubrecht
+     */
+    static final class ResizeBtn extends ToggleButton {
+
+        private final ResizeOption resizeOption;
+
+        ResizeBtn(ResizeOption resizeOption) {
+            super(null, new ImageView(toImage(resizeOption)));
+            this.resizeOption = resizeOption;
+            setTooltip(new Tooltip(resizeOption.getToolTip()));
+        }
+
+        ResizeOption getResizeOption() {
+            return resizeOption;
+        }
+
+        static Image toImage(ResizeOption ro) {
+            if (ro == ResizeOption.SIZE_TO_FIT) {
+                return ResizeOption.Type.CUSTOM.getImage();
+            }
+            return ro.getType().getImage();
+        }
+    }
+
+    /**
+     * Immutable value class describing a single button to resize web browser window.
+     * Taken from NetBeans. Kept GPLwithCPEx license.
+     * Portions Copyrighted 2012 Sun Microsystems, Inc.
+     *
+     * @author S. Aubrecht
+     */
+    static final class ResizeOption {
+
+        private final Type type;
+        private final String displayName;
+        private final int width;
+        private final int height;
+        private final boolean isDefault;
+
+        enum Type {
+            DESKTOP("desktop.png"), 
+            TABLET_PORTRAIT("tabletPortrait.png"), 
+            TABLET_LANDSCAPE("tabletLandscape.png"), 
+            SMARTPHONE_PORTRAIT("handheldPortrait.png"), 
+            SMARTPHONE_LANDSCAPE("handheldLandscape.png"), 
+            WIDESCREEN("widescreen.png"), 
+            NETBOOK("netbook.png"), 
+            CUSTOM("sizeToFit.png");
+            
+            
+            private final String resource;
+
+            private Type(String r) {
+                resource = r;
+            }
+
+            public Image getImage() {
+                return new Image(Type.class.getResourceAsStream(resource));
+            }
+        }
+
+        private ResizeOption(Type type, String displayName, int width, int height, boolean showInToolbar, boolean isDefault) {
+            super();
+            this.type = type;
+            this.displayName = displayName;
+            this.width = width;
+            this.height = height;
+            this.isDefault = isDefault;
+        }
+
+        static List<ResizeOption> loadAll() {
+            List<ResizeOption> res = new ArrayList<ResizeOption>(10);
+            res.add(ResizeOption.create(ResizeOption.Type.DESKTOP, "Desktop", 1280, 1024, true, true));
+            res.add(ResizeOption.create(ResizeOption.Type.TABLET_LANDSCAPE, "Tablet Landscape", 1024, 768, true, true));
+            res.add(ResizeOption.create(ResizeOption.Type.TABLET_PORTRAIT, "Tablet Portrait", 768, 1024, true, true));
+            res.add(ResizeOption.create(ResizeOption.Type.SMARTPHONE_LANDSCAPE, "Smartphone Landscape", 480, 320, true, true));
+            res.add(ResizeOption.create(ResizeOption.Type.SMARTPHONE_PORTRAIT, "Smartphone Portrait", 320, 480, true, true));
+            res.add(ResizeOption.create(ResizeOption.Type.WIDESCREEN, "Widescreen", 1680, 1050, false, true));
+            res.add(ResizeOption.create(ResizeOption.Type.NETBOOK, "Netbook", 1024, 600, false, true));
+            return res;
+        }
+        
+        /**
+         * Creates a new instance.
+         * @param type
+         * @param displayName Display name to show in tooltip, cannot be empty.
+         * @param width Screen width
+         * @param height Screen height
+         * @param showInToolbar True to show in web developer toolbar.
+         * @param isDefault True if this is a predefined option that cannot be removed.
+         * @return New instance.
+         */
+        public static ResizeOption create(Type type, String displayName, int width, int height, boolean showInToolbar, boolean isDefault) {
+            if (width <= 0 || height <= 0) {
+                throw new IllegalArgumentException("Invalid screen dimensions: " + width + " x " + height); //NOI18N
+            }
+            return new ResizeOption(type, displayName, width, height, showInToolbar, isDefault);
+        }
+        /**
+         * An extra option to size the browser content to fit its window.
+         */
+        public static final ResizeOption SIZE_TO_FIT = new ResizeOption(Type.CUSTOM, "Size To Fit", -1, -1, true, true);
+
+        public String getDisplayName() {
+            return displayName;
+        }
+
+        public Type getType() {
+            return type;
+        }
+
+        public int getWidth() {
+            return width;
+        }
+
+        public int getHeight() {
+            return height;
+        }
+
+        public boolean isDefault() {
+            return isDefault;
+        }
+
+        @Override
+        public String toString() {
+            return displayName;
+        }
+
+        public String getToolTip() {
+            if (width < 0 || height < 0) {
+                return displayName;
+            }
+            StringBuilder sb = new StringBuilder();
+            sb.append(width);
+            sb.append(" x "); //NOI18N
+            sb.append(height);
+            sb.append(" ("); //NOI18N
+            sb.append(displayName);
+            sb.append(')'); //NOI18N
+            return sb.toString();
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == null) {
+                return false;
+            }
+            if (getClass() != obj.getClass()) {
+                return false;
+            }
+            final ResizeOption other = (ResizeOption) obj;
+            if (this.type != other.type) {
+                return false;
+            }
+            if ((this.displayName == null) ? (other.displayName != null) : !this.displayName.equals(other.displayName)) {
+                return false;
+            }
+            if (this.width != other.width) {
+                return false;
+            }
+            if (this.height != other.height) {
+                return false;
+            }
+            if (this.isDefault != other.isDefault) {
+                return false;
+            }
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int hash = 7;
+            hash = 11 * hash + (this.type != null ? this.type.hashCode() : 0);
+            hash = 11 * hash + (this.displayName != null ? this.displayName.hashCode() : 0);
+            hash = 11 * hash + this.width;
+            hash = 11 * hash + this.height;
+            hash = 11 * hash + (this.isDefault ? 1 : 0);
+            return hash;
+        }
+    }
+    
+    private void listenOnChanges(boolean turnOn) {
+        try {
+            if (watcher != null) {
+                watcher.close();
+                watcher = null;
+            }
+            final WebEngine eng = webView.getEngine();
+            if (turnOn && eng.getLocation().startsWith("file:")) { // NOI18N
+                watcher = new WatchDir(eng);
+            }
+        } catch (Exception ex) {
+            FXInspect.LOG.log(Level.SEVERE, null, ex);
+        }
+    }
+       private static void enableFirebug(final WebEngine engine) {
+        engine.executeScript("if (!document.getElementById('FirebugLite')){E = document['createElement' + 'NS'] && document.documentElement.namespaceURI;E = E ? document['createElement' + 'NS'](E, 'script') : document['createElement']('script');E['setAttribute']('id', 'FirebugLite');E['setAttribute']('src', 'https://getfirebug.com/' + 'firebug-lite.js' + '#startOpened');E['setAttribute']('FirebugLite', '4');(document['getElementsByTagName']('head')[0] || document['getElementsByTagName']('body')[0]).appendChild(E);E = new Image;E['setAttribute']('src', 'https://getfirebug.com/' + '#startOpened');}"); 
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/java/org/netbeans/html/boot/fx/WatchDir.java
----------------------------------------------------------------------
diff --git a/boot-fx/src/main/java/org/netbeans/html/boot/fx/WatchDir.java b/boot-fx/src/main/java/org/netbeans/html/boot/fx/WatchDir.java
new file mode 100644
index 0000000..25cc5f2
--- /dev/null
+++ b/boot-fx/src/main/java/org/netbeans/html/boot/fx/WatchDir.java
@@ -0,0 +1,117 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.fx;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.file.ClosedWatchServiceException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardWatchEventKinds;
+import java.nio.file.WatchKey;
+import java.nio.file.WatchService;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javafx.application.Platform;
+import javafx.scene.web.WebEngine;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+final class WatchDir implements Runnable {
+    private final Path dir;
+    private final WatchKey key;
+    private final WatchService ws; 
+    private final Thread watcher;
+    private final WebEngine engine;
+    
+    WatchDir(WebEngine eng) throws URISyntaxException, IOException {
+        dir = Paths.get(new URI(eng.getLocation())).getParent();
+        engine = eng;
+        ws = dir.getFileSystem().newWatchService();
+        key = dir.register(ws, 
+            StandardWatchEventKinds.ENTRY_CREATE,
+            StandardWatchEventKinds.ENTRY_DELETE,
+            StandardWatchEventKinds.ENTRY_MODIFY
+        );
+        watcher = new Thread(this, "Watching files in " + dir);
+        watcher.setDaemon(true);
+        watcher.setPriority(Thread.MIN_PRIORITY);
+        watcher.start();
+    }
+
+    public void close() throws IOException {
+        key.cancel();
+        ws.close();
+        watcher.interrupt();
+    }
+
+    @Override
+    public void run() {
+        if (Platform.isFxApplicationThread()) {
+            engine.reload();
+            return;
+        }
+        try {
+            while (key.isValid()) {
+                WatchKey changed;
+                try {
+                    changed = ws.take();
+                    if (changed != key || changed.pollEvents().isEmpty()) {
+                        continue;
+                    }
+                } catch (ClosedWatchServiceException ex) {
+                    continue;
+                }
+                Platform.runLater(this);
+                if (!key.reset()) {
+                    break;
+                }
+            }
+        } catch (InterruptedException ex) {
+            FXInspect.LOG.log(Level.SEVERE, null, ex);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/resources/org/netbeans/html/boot/fx/Bundle.properties
----------------------------------------------------------------------
diff --git a/boot-fx/src/main/resources/org/netbeans/html/boot/fx/Bundle.properties b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/Bundle.properties
new file mode 100644
index 0000000..7ebe82d
--- /dev/null
+++ b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/Bundle.properties
@@ -0,0 +1,54 @@
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+#
+# Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+# Other names may be trademarks of their respective owners.
+#
+# The contents of this file are subject to the terms of either the GNU
+# General Public License Version 2 only ("GPL") or the Common
+# Development and Distribution License("CDDL") (collectively, the
+# "License"). You may not use this file except in compliance with the
+# License. You can obtain a copy of the License at
+# http://www.netbeans.org/cddl-gplv2.html
+# or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+# specific language governing permissions and limitations under the
+# License.  When distributing the software, include this License Header
+# Notice in each file and include the License file at
+# nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle in the GPL Version 2 section of the License file that
+# accompanied this code. If applicable, add the following below the
+# License Header, with the fields enclosed by brackets [] replaced by
+# your own identifying information:
+# "Portions Copyrighted [year] [name of copyright owner]"
+#
+# Contributor(s):
+#
+# The Original Software is NetBeans. The Initial Developer of the Original
+# Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+#
+# If you wish your version of this file to be governed by only the CDDL
+# or only the GPL Version 2, indicate your decision by adding
+# "[Contributor] elects to include this software in this distribution
+# under the [CDDL or GPL Version 2] license." If you do not indicate a
+# single choice of license, a recipient has the option to distribute
+# your version of this file under either the CDDL, the GPL Version 2 or
+# to extend the choice of license to its licensees as provided above.
+# However, if you add GPL Version 2 code and therefore, elected the GPL
+# Version 2 license, then the option applies only if the new code is
+# made subject to such option by the copyright holder.
+#
+
+AlertTitle=Warning
+AlertCloseButton=Close
+
+ConfirmTitle=Question
+ConfirmOKButton=OK
+ConfirmCancelButton=Cancel
+
+PromptTitle=Question
+PromptOKButton=OK
+PromptCancelButton=Cancel
+

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/resources/org/netbeans/html/boot/fx/desktop.png
----------------------------------------------------------------------
diff --git a/boot-fx/src/main/resources/org/netbeans/html/boot/fx/desktop.png b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/desktop.png
new file mode 100644
index 0000000..b295e4b
Binary files /dev/null and b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/desktop.png differ

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/resources/org/netbeans/html/boot/fx/handheldLandscape.png
----------------------------------------------------------------------
diff --git a/boot-fx/src/main/resources/org/netbeans/html/boot/fx/handheldLandscape.png b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/handheldLandscape.png
new file mode 100644
index 0000000..ce5d64e
Binary files /dev/null and b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/handheldLandscape.png differ

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/resources/org/netbeans/html/boot/fx/handheldPortrait.png
----------------------------------------------------------------------
diff --git a/boot-fx/src/main/resources/org/netbeans/html/boot/fx/handheldPortrait.png b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/handheldPortrait.png
new file mode 100644
index 0000000..686bea8
Binary files /dev/null and b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/handheldPortrait.png differ

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/resources/org/netbeans/html/boot/fx/netbook.png
----------------------------------------------------------------------
diff --git a/boot-fx/src/main/resources/org/netbeans/html/boot/fx/netbook.png b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/netbook.png
new file mode 100644
index 0000000..53838ba
Binary files /dev/null and b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/netbook.png differ

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/resources/org/netbeans/html/boot/fx/selectionMode.png
----------------------------------------------------------------------
diff --git a/boot-fx/src/main/resources/org/netbeans/html/boot/fx/selectionMode.png b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/selectionMode.png
new file mode 100644
index 0000000..d2e398c
Binary files /dev/null and b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/selectionMode.png differ

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/resources/org/netbeans/html/boot/fx/sizeToFit.png
----------------------------------------------------------------------
diff --git a/boot-fx/src/main/resources/org/netbeans/html/boot/fx/sizeToFit.png b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/sizeToFit.png
new file mode 100644
index 0000000..5fec5a4
Binary files /dev/null and b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/sizeToFit.png differ

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/resources/org/netbeans/html/boot/fx/tabletLandscape.png
----------------------------------------------------------------------
diff --git a/boot-fx/src/main/resources/org/netbeans/html/boot/fx/tabletLandscape.png b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/tabletLandscape.png
new file mode 100644
index 0000000..89f7f61
Binary files /dev/null and b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/tabletLandscape.png differ

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/resources/org/netbeans/html/boot/fx/tabletPortrait.png
----------------------------------------------------------------------
diff --git a/boot-fx/src/main/resources/org/netbeans/html/boot/fx/tabletPortrait.png b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/tabletPortrait.png
new file mode 100644
index 0000000..fb94771
Binary files /dev/null and b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/tabletPortrait.png differ

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/resources/org/netbeans/html/boot/fx/widescreen.png
----------------------------------------------------------------------
diff --git a/boot-fx/src/main/resources/org/netbeans/html/boot/fx/widescreen.png b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/widescreen.png
new file mode 100644
index 0000000..933e01f
Binary files /dev/null and b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/widescreen.png differ

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/test/java/net/java/html/boot/fx/FXBrowsersOnResourceTest.java
----------------------------------------------------------------------
diff --git a/boot-fx/src/test/java/net/java/html/boot/fx/FXBrowsersOnResourceTest.java b/boot-fx/src/test/java/net/java/html/boot/fx/FXBrowsersOnResourceTest.java
new file mode 100644
index 0000000..1400ad8
--- /dev/null
+++ b/boot-fx/src/test/java/net/java/html/boot/fx/FXBrowsersOnResourceTest.java
@@ -0,0 +1,209 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.boot.fx;
+
+import java.net.URL;
+import java.util.concurrent.CountDownLatch;
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.scene.Scene;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.web.WebView;
+import javafx.stage.Stage;
+import net.java.html.js.JavaScriptBody;
+import net.java.html.js.JavaScriptResource;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNotSame;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class FXBrowsersOnResourceTest {
+    
+    public FXBrowsersOnResourceTest() {
+    }
+    
+    @BeforeClass public void initFX() throws Throwable {
+        new Thread("initFX") {
+            @Override
+            public void run() {
+                if (Platform.isFxApplicationThread()) {
+                    new App().start(new Stage());
+                } else {
+                    try {
+                        App.launch(App.class);
+                    } catch (IllegalStateException ex) {
+                        Platform.runLater(this);
+                    }
+                }
+            }
+        }.start();
+        App.CDL.await();
+    }
+
+    @Test
+    public void behaviorOfTwoWebViewsAtOnce() throws Throwable {
+        class R implements Runnable {
+            CountDownLatch DONE = new CountDownLatch(1);
+            Throwable t;
+
+            @Override
+            public void run() {
+                try {
+                    doTest();
+                } catch (Throwable ex) {
+                    t = ex;
+                } finally {
+                    DONE.countDown();
+                }
+            }
+            
+            private void doTest() throws Throwable {
+                URL u = FXBrowsersOnResourceTest.class.getResource("/org/netbeans/html/boot/fx/empty.html");
+                assertNotNull(u, "URL found");
+                FXBrowsers.load(App.getV1(), u, OnPages.class, "first");
+                
+            }
+        }
+        R run = new R();
+        Platform.runLater(run);
+        run.DONE.await();
+        for (int i = 0; i < 100; i++) {
+            if (run.t != null) {
+                throw run.t;
+            }
+            if (System.getProperty("finalSecond") == null) {
+                Thread.sleep(100);
+            }
+        }
+        
+        
+        
+        assertEquals(Integer.getInteger("finalFirst"), Integer.valueOf(3), "Three times in view one");
+        assertEquals(Integer.getInteger("finalSecond"), Integer.valueOf(2), "Two times in view one");
+    }
+
+    @JavaScriptResource("wnd.js")
+    public static class OnPages {
+        static Class<?> first;
+        static Object firstWindow;
+        
+        public static void first() {
+            first = OnPages.class;
+            firstWindow = window();
+            assertNotNull(firstWindow, "First window found");
+            
+            assertEquals(increment(), 1, "Now it is one");
+            
+            URL u = FXBrowsersOnResourceTest.class.getResource("/org/netbeans/html/boot/fx/empty.html");
+            assertNotNull(u, "URL found");
+            FXBrowsers.load(App.getV2(), u, OnPages.class, "second", "Hello");
+            
+            assertEquals(increment(), 2, "Now it is two and not influenced by second view");
+            System.setProperty("finalFirst", "" + increment());
+        }
+        
+        public static void second(String... args) {
+            assertEquals(args.length, 1, "One string argument");
+            assertEquals(args[0], "Hello", "It is hello");
+            assertEquals(first, OnPages.class, "Both views share the same classloader");
+            
+            Object window = window();
+            assertNotNull(window, "Some window found");
+            assertNotNull(firstWindow, "First window is known");
+            assertNotSame(firstWindow, window, "The window objects should be different");
+            
+            assertEquals(increment(), 1, "Counting starts from zero");
+            System.setProperty("finalSecond", "" + increment());
+        }
+        
+        @JavaScriptBody(args = {}, body = "return wnd;")
+        private static native Object window();
+        
+        @JavaScriptBody(args = {}, body = ""
+            + "if (wnd.cnt) return ++wnd.cnt;"
+            + "return wnd.cnt = 1;"
+        )
+        private static native int increment();
+    }
+    
+    public static class App extends Application {
+        static final CountDownLatch CDL = new CountDownLatch(1);
+        private static BorderPane pane;
+
+        /**
+         * @return the v1
+         */
+        static WebView getV1() {
+            return (WebView)System.getProperties().get("v1");
+        }
+
+        /**
+         * @return the v2
+         */
+        static WebView getV2() {
+            return (WebView)System.getProperties().get("v2");
+        }
+
+        @Override
+        public void start(Stage stage) {
+            pane= new BorderPane();
+            Scene scene = new Scene(pane, 800, 600);
+            stage.setScene(scene);
+            
+            System.getProperties().put("v1", new WebView());
+            System.getProperties().put("v2", new WebView());
+
+            pane.setCenter(getV1());
+            pane.setBottom(getV2());
+
+            CDL.countDown();
+        }
+        
+        
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/test/java/net/java/html/boot/fx/FXBrowsersTest.java
----------------------------------------------------------------------
diff --git a/boot-fx/src/test/java/net/java/html/boot/fx/FXBrowsersTest.java b/boot-fx/src/test/java/net/java/html/boot/fx/FXBrowsersTest.java
new file mode 100644
index 0000000..2498c56
--- /dev/null
+++ b/boot-fx/src/test/java/net/java/html/boot/fx/FXBrowsersTest.java
@@ -0,0 +1,253 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.boot.fx;
+
+import java.net.URL;
+import java.util.concurrent.CountDownLatch;
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.scene.Scene;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.web.WebView;
+import javafx.stage.Stage;
+import net.java.html.BrwsrCtx;
+import net.java.html.js.JavaScriptBody;
+import static org.testng.Assert.*;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class FXBrowsersTest {
+    private static CountDownLatch PROPERTY_SET;
+
+    public FXBrowsersTest() {
+    }
+    
+    @BeforeClass public void initFX() throws Throwable {
+        new Thread("initFX") {
+            @Override
+            public void run() {
+                if (Platform.isFxApplicationThread()) {
+                    new App().start(new Stage());
+                } else {
+                    try {
+                        App.launch(App.class);
+                    } catch (IllegalStateException ex) {
+                        Platform.runLater(this);
+                    }
+                }
+            }
+        }.start();
+        App.CDL.await();
+    }
+    
+    @JavaScriptBody(args = {  }, body = "return true;")
+    static boolean inJS() {
+        return false;
+    }
+
+    @Test
+    public void brwsrCtxExecute() throws Throwable {
+        assertFalse(inJS(), "We aren't in JS now");
+        final CountDownLatch init = new CountDownLatch(1);
+        final BrwsrCtx[] ctx = { null };
+        FXBrowsers.runInBrowser(App.getV1(), new Runnable() {
+            @Override
+            public void run() {
+                assertTrue(inJS(), "We are in JS context now");
+                ctx[0] = BrwsrCtx.findDefault(FXBrowsersTest.class);
+                init.countDown();
+            }
+        });
+        init.await();
+
+        final CountDownLatch cdl = new CountDownLatch(1);
+        class R implements Runnable {
+            @Override
+            public void run() {
+                if (Platform.isFxApplicationThread()) {
+                    assertTrue(inJS());
+                    cdl.countDown();
+                } else {
+                    ctx[0].execute(this);
+                }
+            }
+        }
+        new Thread(new R(), "Background thread").start();
+
+        cdl.await();
+    }
+
+    @Test
+    public void behaviorOfTwoWebViewsAtOnce() throws Throwable {
+        class R implements Runnable {
+            Throwable t;
+
+            @Override
+            public void run() {
+                try {
+                    doTest();
+                } catch (Throwable ex) {
+                    t = ex;
+                }
+            }
+            
+            private void doTest() throws Throwable {
+                URL u = FXBrowsersTest.class.getResource("/org/netbeans/html/boot/fx/empty.html");
+                assertNotNull(u, "URL found");
+                FXBrowsers.load(App.getV1(), u, OnPages.class, "first");
+            }
+        }
+        R run = new R();
+        PROPERTY_SET = new CountDownLatch(2);
+        Platform.runLater(run);
+        PROPERTY_SET.await();
+        
+        assertEquals(Integer.getInteger("finalFirst"), Integer.valueOf(3), "Three times in view one");
+        assertEquals(Integer.getInteger("finalSecond"), Integer.valueOf(2), "Two times in view one");
+
+        final CountDownLatch finish = new CountDownLatch(1);
+        final Object[] three = { 0 };
+        assertFalse(Platform.isFxApplicationThread());
+        FXBrowsers.runInBrowser(App.getV1(), new Runnable() {
+            @Override
+            public void run() {
+                assertTrue(Platform.isFxApplicationThread());
+                three[0] = App.getV1().getEngine().executeScript("window.cntBrwsr");
+                finish.countDown();
+            }
+        });
+        finish.await();
+        
+        assertEquals(three[0], Integer.valueOf(3));
+    }
+    
+    public static class OnPages {
+        static Class<?> first;
+        static Object firstWindow;
+        
+        public static void first() {
+            first = OnPages.class;
+            firstWindow = window();
+            assertNotNull(firstWindow, "First window found");
+            
+            assertEquals(increment(), 1, "Now it is one");
+            
+            URL u = FXBrowsersTest.class.getResource("/org/netbeans/html/boot/fx/empty.html");
+            assertNotNull(u, "URL found");
+            FXBrowsers.load(App.getV2(), u, new Runnable() {
+                @Override
+                public void run() {
+                    OnPages.second("Hello");
+                }
+            });
+            
+            assertEquals(increment(), 2, "Now it is two and not influenced by second view");
+            System.setProperty("finalFirst", "" + increment());
+            PROPERTY_SET.countDown();
+        }
+        
+        public static void second(String... args) {
+            assertEquals(args.length, 1, "One string argument");
+            assertEquals(args[0], "Hello", "It is hello");
+            assertEquals(first, OnPages.class, "Both views share the same classloader");
+            
+            Object window = window();
+            assertNotNull(window, "Some window found");
+            assertNotNull(firstWindow, "First window is known");
+            assertNotSame(firstWindow, window, "The window objects should be different");
+            
+            assertEquals(increment(), 1, "Counting starts from zero");
+            System.setProperty("finalSecond", "" + increment());
+            PROPERTY_SET.countDown();
+        }
+        
+        @JavaScriptBody(args = {}, body = "return window;")
+        private static native Object window();
+        
+        @JavaScriptBody(args = {}, body = ""
+            + "if (window.cntBrwsr) return ++window.cntBrwsr;"
+            + "return window.cntBrwsr = 1;"
+        )
+        private static native int increment();
+    }
+    
+    public static class App extends Application {
+        static final CountDownLatch CDL = new CountDownLatch(1);
+        private static BorderPane pane;
+
+        /**
+         * @return the v1
+         */
+        static WebView getV1() {
+            return (WebView)System.getProperties().get("v1");
+        }
+
+        /**
+         * @return the v2
+         */
+        static WebView getV2() {
+            return (WebView)System.getProperties().get("v2");
+        }
+
+        @Override
+        public void start(Stage stage) {
+            pane= new BorderPane();
+            Scene scene = new Scene(pane, 800, 600);
+            stage.setScene(scene);
+            
+            System.getProperties().put("v1", new WebView());
+            System.getProperties().put("v2", new WebView());
+
+            pane.setCenter(getV1());
+            pane.setBottom(getV2());
+
+            CDL.countDown();
+        }
+        
+        
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/test/java/org/netbeans/html/boot/fx/FXBrwsrTest.java
----------------------------------------------------------------------
diff --git a/boot-fx/src/test/java/org/netbeans/html/boot/fx/FXBrwsrTest.java b/boot-fx/src/test/java/org/netbeans/html/boot/fx/FXBrwsrTest.java
new file mode 100644
index 0000000..1f52998
--- /dev/null
+++ b/boot-fx/src/test/java/org/netbeans/html/boot/fx/FXBrwsrTest.java
@@ -0,0 +1,84 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.fx;
+
+import org.sample.app.pkg.SampleApp;
+import static org.testng.Assert.assertEquals;
+import org.testng.annotations.Test;
+
+public class FXBrwsrTest {
+
+    public FXBrwsrTest() {
+    }
+
+    @Test
+    public void testFindCalleeClassName() throws InterruptedException {
+        String callee = invokeMain();
+        assertEquals(callee, SampleApp.class.getName(), "Callee is found correctly");
+    }
+
+    synchronized static String invokeMain() throws InterruptedException {
+        new Thread("starting main") {
+            @Override
+            public void run() {
+                SampleApp.main();
+            }
+        }.start();
+        for (;;) {
+            String callee = System.getProperty("callee");
+            if (callee != null) {
+                return callee;
+            }
+            FXBrwsrTest.class.wait();
+        }
+    }
+
+
+    public static void computeCalleeClassName() {
+        String name = FXBrwsr.findCalleeClassName();
+        System.setProperty("callee", name);
+        synchronized (FXBrwsrTest.class) {
+            FXBrwsrTest.class.notifyAll();
+        }
+    }
+}


[03/24] incubator-netbeans-html4j git commit: [INFRA-15006] Initial donation of HTML/Java NetBeans API. Equivalent to the content of html4j-donation-review.zip donated as part of ApacheNetBeansDonation1.zip with SHA256 being 7f2ca0f61953a190613c9a0fbcc1b

Posted by jt...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusFX.java
----------------------------------------------------------------------
diff --git a/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusFX.java b/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusFX.java
new file mode 100644
index 0000000..1bc5613
--- /dev/null
+++ b/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusFX.java
@@ -0,0 +1,125 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.wstyrus;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import javafx.application.Platform;
+import org.netbeans.html.boot.impl.FnContext;
+import org.netbeans.html.boot.spi.Fn;
+import org.testng.ITest;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public final class TyrusFX implements ITest, Runnable {
+    private final Fn.Presenter p;
+    private final Method m;
+    private Object result;
+    private Object inst;
+    private int count;
+
+    TyrusFX(Fn.Presenter p, Method m) {
+        this.p = p;
+        this.m = m;
+    }
+
+    @Override
+    public String getTestName() {
+        return m.getName();
+    }
+
+    @Test
+    public synchronized void executeTest() throws Exception {
+        if (result == null) {
+            Platform.runLater(this);
+            wait();
+        }
+        if (result instanceof Exception) {
+            throw (Exception)result;
+        }
+        if (result instanceof Error) {
+            throw (Error)result;
+        }
+    }
+
+    @Override
+    public synchronized void run() {
+        boolean notify = true;
+        try {
+            FnContext.currentPresenter(p);
+            if (inst == null) {
+                inst = m.getDeclaringClass().newInstance();
+            }
+            result = m.invoke(inst);
+            if (result == null) {
+                result = this;
+            }
+        } catch (InvocationTargetException ex) {
+            Throwable r = ex.getTargetException();
+            if (r instanceof InterruptedException) {
+                if (count++ < 10000) {
+                    notify = false;
+                    try {
+                        Thread.sleep(100);
+                    } catch (Exception ex1) {
+                        // ignore and continue
+                    }
+                    Platform.runLater(this);
+                    return;
+                }
+            }
+            result = r;
+        } catch (Exception ex) {
+            result = ex;
+        } finally {
+            if (notify) {
+                notifyAll();
+            }
+            FnContext.currentPresenter(null);
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusKnockoutTest.java
----------------------------------------------------------------------
diff --git a/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusKnockoutTest.java b/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusKnockoutTest.java
new file mode 100644
index 0000000..9f20e57
--- /dev/null
+++ b/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusKnockoutTest.java
@@ -0,0 +1,219 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.wstyrus;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import net.java.html.BrwsrCtx;
+import net.java.html.boot.BrowserBuilder;
+import net.java.html.js.JavaScriptBody;
+import org.netbeans.html.boot.spi.Fn;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.json.spi.Technology;
+import org.netbeans.html.json.spi.Transfer;
+import org.netbeans.html.json.spi.WSTransfer;
+import org.netbeans.html.json.tck.KOTest;
+import org.netbeans.html.json.tck.KnockoutTCK;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.netbeans.html.boot.impl.FnContext;
+import org.netbeans.html.ko4j.KO4J;
+import org.openide.util.lookup.ServiceProvider;
+import org.testng.Assert;
+import static org.testng.Assert.*;
+import org.testng.annotations.Factory;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+@ServiceProvider(service = KnockoutTCK.class)
+public final class TyrusKnockoutTest extends KnockoutTCK {
+    private static Class<?> browserClass;
+    private static Fn.Presenter browserContext;
+    
+    public TyrusKnockoutTest() {
+    }
+    
+    @Factory public static Object[] compatibilityTests() throws Exception {
+        Class[] arr = testClasses();
+        for (int i = 0; i < arr.length; i++) {
+            assertEquals(
+                arr[i].getClassLoader(),
+                TyrusKnockoutTest.class.getClassLoader(),
+                "All classes loaded by the same classloader"
+            );
+        }
+        
+        URI uri = TyrusDynamicHTTP.initServer();
+    
+        final BrowserBuilder bb = BrowserBuilder.newBrowser().loadClass(TyrusKnockoutTest.class).
+            loadPage(uri.toString()).
+            invoke("initialized");
+        
+        Executors.newSingleThreadExecutor().submit(new Runnable() {
+            @Override
+            public void run() {
+                bb.showAndWait();
+            }
+        });
+        
+        ClassLoader l = getClassLoader();
+        List<Object> res = new ArrayList<Object>();
+        for (int i = 0; i < arr.length; i++) {
+            Class<?> c = Class.forName(arr[i].getName(), true, l);
+            Class<? extends Annotation> koTest = 
+                c.getClassLoader().loadClass(KOTest.class.getName()).
+                asSubclass(Annotation.class);
+            for (Method m : c.getMethods()) {
+                if (m.getAnnotation(koTest) != null) {
+                    res.add(new TyrusFX(browserContext, m));
+                }
+            }
+        }
+        return res.toArray();
+    }
+
+    static synchronized ClassLoader getClassLoader() throws InterruptedException {
+        while (browserClass == null) {
+            TyrusKnockoutTest.class.wait();
+        }
+        return browserClass.getClassLoader();
+    }
+    
+    public static synchronized void initialized(Class<?> browserCls) throws Exception {
+        browserClass = browserCls;
+        browserContext = Fn.activePresenter();
+        TyrusKnockoutTest.class.notifyAll();
+    }
+    
+    public static void initialized() throws Exception {
+        Assert.assertSame(
+            TyrusKnockoutTest.class.getClassLoader(),
+            ClassLoader.getSystemClassLoader(),
+            "No special classloaders"
+        );
+        TyrusKnockoutTest.initialized(TyrusKnockoutTest.class);
+    }
+    
+    @Override
+    public BrwsrCtx createContext() {
+        KO4J ko = new KO4J(browserContext);
+        TyrusContext tc = new TyrusContext();
+        Contexts.Builder cb = Contexts.newBuilder().
+            register(Technology.class, ko.knockout(), 10).
+            register(Transfer.class, tc, 10).
+            register(WSTransfer.class, tc, 10).
+            register(Executor.class, (Executor)browserContext, 10).
+            register(Fn.Presenter.class, (Fn.Presenter)browserContext, 10);
+                
+        return cb.build();
+    }
+
+    @Override
+    public Object createJSON(Map<String, Object> values) {
+        JSONObject json = new JSONObject();
+        for (Map.Entry<String, Object> entry : values.entrySet()) {
+            try {
+                json.put(entry.getKey(), entry.getValue());
+            } catch (JSONException ex) {
+                throw new IllegalStateException(ex);
+            }
+        }
+        return json;
+    }
+
+    @Override
+    @JavaScriptBody(args = { "s", "args" }, body = ""
+        + "var f = new Function(s); "
+        + "return f.apply(null, args);"
+    )
+    public native Object executeScript(String script, Object[] arguments);
+
+    @JavaScriptBody(args = {  }, body = 
+          "var h;"
+        + "if (!!window && !!window.location && !!window.location.href)\n"
+        + "  h = window.location.href;\n"
+        + "else "
+        + "  h = null;"
+        + "return h;\n"
+    )
+    private static native String findBaseURL();
+    
+    @Override
+    public URI prepareURL(String content, String mimeType, String[] parameters) {
+        try {
+            final URL baseURL = new URL(findBaseURL());
+            StringBuilder sb = new StringBuilder();
+            sb.append("/dynamic?mimeType=").append(mimeType);
+            for (int i = 0; i < parameters.length; i++) {
+                sb.append("&param" + i).append("=").append(parameters[i]);
+            }
+            String mangle = content.replace("\n", "%0a")
+                .replace("\"", "\\\"").replace(" ", "%20");
+            sb.append("&content=").append(mangle);
+
+            URL query = new URL(baseURL, sb.toString());
+            URLConnection c = query.openConnection();
+            BufferedReader br = new BufferedReader(new InputStreamReader(c.getInputStream()));
+            URI connectTo = new URI(br.readLine());
+            return connectTo;
+        } catch (IOException ex) {
+            throw new IllegalStateException(ex);
+        } catch (URISyntaxException ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko-ws-tyrus/src/test/resources/org/netbeans/html/wstyrus/test.html
----------------------------------------------------------------------
diff --git a/ko-ws-tyrus/src/test/resources/org/netbeans/html/wstyrus/test.html b/ko-ws-tyrus/src/test/resources/org/netbeans/html/wstyrus/test.html
new file mode 100644
index 0000000..f4e58fe
--- /dev/null
+++ b/ko-ws-tyrus/src/test/resources/org/netbeans/html/wstyrus/test.html
@@ -0,0 +1,56 @@
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>Tyrus WebSockets Execution Harness</title>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <meta name="viewport" content="width=device-width">
+    </head>
+    <body>
+        <h1>Tyrus WebSockets Execution Harness</h1>
+    </body>
+    <script></script>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko4j/pom.xml
----------------------------------------------------------------------
diff --git a/ko4j/pom.xml b/ko4j/pom.xml
new file mode 100644
index 0000000..5ba42a3
--- /dev/null
+++ b/ko4j/pom.xml
@@ -0,0 +1,179 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2016 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<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">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.netbeans.html</groupId>
+    <artifactId>pom</artifactId>
+    <version>2.0-SNAPSHOT</version>
+  </parent>
+  <groupId>org.netbeans.html</groupId>
+  <artifactId>ko4j</artifactId>
+  <version>2.0-SNAPSHOT</version>
+  <packaging>bundle</packaging>
+  <name>Knockout.js for Java</name>
+  <url>http://maven.apache.org</url>
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <bundleSymbolicName>org.netbeans.html.ko4j</bundleSymbolicName>
+    <netbeans.compile.on.save>none</netbeans.compile.on.save> 
+  </properties>
+  <build>
+      <plugins>
+          <plugin>
+              <groupId>org.apache.felix</groupId>
+              <artifactId>maven-bundle-plugin</artifactId>
+              <configuration>
+                  <instructions>
+                      <Require-Capability>osgi.extender;resolution:=optional;filter:="(osgi.extender=osgi.serviceloader.registrar)"</Require-Capability>
+                      <Provide-Capability>osgi.serviceloader;osgi.serviceloader=org.netbeans.html.context.spi.Contexts$Provider</Provide-Capability>
+                  </instructions>
+              </configuration>
+          </plugin>
+          <plugin>
+              <groupId>org.netbeans.html</groupId>
+              <artifactId>html4j-maven-plugin</artifactId>
+          </plugin>
+          <plugin>
+              <groupId>org.apache.maven.plugins</groupId>
+              <artifactId>maven-javadoc-plugin</artifactId>
+              <configuration>
+                  <skip>false</skip>
+              </configuration>
+          </plugin>
+          <plugin>
+              <inherited>false</inherited>
+              <groupId>org.apache.maven.plugins</groupId>
+              <artifactId>maven-dependency-plugin</artifactId>
+              <version>2.10</version>
+              <executions>
+                  <execution>
+                      <id>knockout.js</id>
+                      <goals>
+                          <goal>unpack</goal>
+                      </goals>
+                      <phase>generate-resources</phase>
+                      <configuration>
+                          <artifactItems>
+                              <artifactItem>
+                                  <groupId>org.netbeans.html</groupId>
+                                  <artifactId>ko4j</artifactId>
+                                  <version>1.3</version>
+                                  <type>jar</type>
+                                  <overWrite>false</overWrite>
+                                  <outputDirectory>${project.build.directory}/classes</outputDirectory>
+                                  <includes>**/*.js</includes>
+                              </artifactItem>
+                          </artifactItems>
+                          <artifact>
+                          </artifact>
+                      </configuration>
+                  </execution>
+              </executions>
+          </plugin>
+      </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>org.netbeans.html</groupId>
+      <artifactId>net.java.html.json</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.testng</groupId>
+      <artifactId>testng</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>net.java.html.json.tck</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.netbeans.api</groupId>
+      <artifactId>org-openide-util-lookup</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.netbeans.html</groupId>
+      <artifactId>net.java.html.boot</artifactId>
+      <version>${project.version}</version>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>net.java.html.boot.fx</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.glassfish.grizzly</groupId>
+      <artifactId>grizzly-http-server</artifactId>
+      <version>${grizzly.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.glassfish.grizzly</groupId>
+      <artifactId>grizzly-websockets-server</artifactId>
+      <version>${grizzly.version}</version>
+      <scope>test</scope>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+        <groupId>org.glassfish.grizzly</groupId>
+        <artifactId>grizzly-http-servlet</artifactId>
+        <version>${grizzly.version}</version>
+        <scope>test</scope>
+    </dependency>    
+    <dependency>
+        <groupId>javax.servlet</groupId>
+        <artifactId>javax.servlet-api</artifactId>
+        <scope>test</scope>
+    </dependency>
+  </dependencies>
+    <description>Binds net.java.html.json APIs together with knockout.js</description>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko4j/src/main/java/org/netbeans/html/ko4j/KO4J.java
----------------------------------------------------------------------
diff --git a/ko4j/src/main/java/org/netbeans/html/ko4j/KO4J.java b/ko4j/src/main/java/org/netbeans/html/ko4j/KO4J.java
new file mode 100644
index 0000000..8aa98c5
--- /dev/null
+++ b/ko4j/src/main/java/org/netbeans/html/ko4j/KO4J.java
@@ -0,0 +1,149 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.ko4j;
+
+import java.util.logging.Logger;
+import net.java.html.json.Model;
+import net.java.html.json.OnReceive;
+import org.netbeans.html.boot.spi.Fn;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.context.spi.Contexts.Provider;
+import org.netbeans.html.json.spi.Technology;
+import org.netbeans.html.json.spi.Transfer;
+import org.netbeans.html.json.spi.WSTransfer;
+import org.openide.util.lookup.ServiceProvider;
+
+/** Support for <a href="http://knockoutjs.com">knockout.js</a>
+ * and its Java binding via {@link Model model classes}.
+ * Registers {@link Provider}, so {@link java.util.ServiceLoader} can find it.
+ * The provider registers following technologies:
+ * <ul>
+ * <li><b>ko4j</b> - bindings for <a href="http://knockoutjs.com">knockout.js</a>
+ *   and the classes generated by the {@link Model} annotation.
+ * </li>
+ * <li><b>xhr</b> - <a href="http://www.w3.org/TR/XMLHttpRequest/">XMLHttpRequest</a>
+ *   based implementation for <em>REST</em> calls 
+ *   (<b>GET</b>, <b>PUT</b>, <b>POST</b>, <b>DELETE</b> methods) 
+ *   for {@link OnReceive} annotation.
+ * </li>
+ * <li><b>websocket</b> - 
+ *   native browser <a href="http://www.w3.org/TR/websockets/">websockets</a>
+ *   based implementation for {@link OnReceive} annotation and its <b>WebSocket</b>
+ *   subprotocol.
+ * </li>
+ * </ul>
+ *
+ * @author Jaroslav Tulach
+ * @since 0.7
+ */
+@ServiceProvider(service = Provider.class)
+public final class KO4J implements Provider {
+    static final Logger LOG = Logger.getLogger(KOSockets.class.getName());
+    private KOTech ko4j;
+    private KOTransfer trans;
+    private KOSockets socks;
+    
+    public KO4J() {
+        this(null);
+    }
+
+    @Deprecated
+    public KO4J(Fn.Presenter presenter) {
+    }
+    
+    /** Return instance of the knockout.js for Java technology.
+     * @return non-null instance
+     */
+    public Technology knockout() {
+        if (ko4j == null) {
+            ko4j = new KOTech();
+        }
+        return ko4j;
+    }
+    
+    /** Browser based implementation of transfer interface. Uses
+     * browser method to convert string to JSON.
+     * 
+     * @return non-null instance
+     */
+    public Transfer transfer() {
+        if (trans == null) {
+            trans = new KOTransfer();
+        }
+        return trans;
+    }
+    
+    /** Returns browser based implementation of websocket transfer.
+     * If available (for example JavaFX WebView on JDK7 does not have
+     * this implementation).
+     * 
+     * @return an instance or <code>null</code>, if there is no
+     *   <code>WebSocket</code> object in the browser
+     */
+    public WSTransfer<?> websockets() {
+        if (!KOSockets.areWebSocketsSupported()) {
+            return null;
+        }
+        if (socks == null) {
+            socks = new KOSockets();
+        }
+        return socks;
+    }
+
+    /** Registers technologies at position 100:
+     * <ul>
+     *   <li>{@link #knockout()}</li>
+     *   <li>{@link #transfer()}</li>
+     *   <li>{@link #websockets()()} - if browser supports web sockets</li>
+     * </ul>
+     * @param context the context to register to
+     * @param requestor the class requesting the registration
+     */
+    @Override
+    public void fillContext(Contexts.Builder context, Class<?> requestor) {
+        context.register(Technology.class, knockout(), 100);
+        context.register(Transfer.class, transfer(), 100);
+        context.register(WSTransfer.class, websockets(), 100);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko4j/src/main/java/org/netbeans/html/ko4j/KOSockets.java
----------------------------------------------------------------------
diff --git a/ko4j/src/main/java/org/netbeans/html/ko4j/KOSockets.java b/ko4j/src/main/java/org/netbeans/html/ko4j/KOSockets.java
new file mode 100644
index 0000000..bb7c49e
--- /dev/null
+++ b/ko4j/src/main/java/org/netbeans/html/ko4j/KOSockets.java
@@ -0,0 +1,81 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.ko4j;
+
+import net.java.html.js.JavaScriptBody;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.json.spi.JSONCall;
+import org.netbeans.html.json.spi.WSTransfer;
+
+/** This is an implementation package - just
+ * include its JAR on classpath and use official {@link Context} API
+ * to access the functionality.
+ *
+ * @author Jaroslav Tulach
+ */
+@Contexts.Id("websocket")
+final class KOSockets
+implements WSTransfer<LoadWS> {
+    KOSockets() {
+    }
+    
+    @Override
+    public LoadWS open(String url, JSONCall onReply) {
+        return new LoadWS(onReply, url);
+    }
+
+    @Override
+    public void send(LoadWS socket, JSONCall data) {
+        socket.send(data);
+    }
+
+    @Override
+    public void close(LoadWS socket) {
+        socket.close();
+    }
+
+    @JavaScriptBody(args = {}, body = "if (window['WebSocket']) return true; else return false;")
+    static final boolean areWebSocketsSupported() {
+        return false;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko4j/src/main/java/org/netbeans/html/ko4j/KOTech.java
----------------------------------------------------------------------
diff --git a/ko4j/src/main/java/org/netbeans/html/ko4j/KOTech.java b/ko4j/src/main/java/org/netbeans/html/ko4j/KOTech.java
new file mode 100644
index 0000000..b598f82
--- /dev/null
+++ b/ko4j/src/main/java/org/netbeans/html/ko4j/KOTech.java
@@ -0,0 +1,176 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.ko4j;
+
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.json.spi.FunctionBinding;
+import org.netbeans.html.json.spi.PropertyBinding;
+import org.netbeans.html.json.spi.Technology;
+import static org.netbeans.html.ko4j.KO4J.LOG;
+
+/** This is an implementation package - just
+ * include its JAR on classpath and use official {@link Context} API
+ * to access the functionality.
+ * <p>
+ *
+ * @author Jaroslav Tulach
+ */
+@Contexts.Id("ko4j")
+final class KOTech
+implements Technology.BatchCopy<Object>, Technology.ValueMutated<Object>, Technology.ApplyId<Object> {
+    private Object[] jsObjects;
+    private int jsIndex;
+
+    public KOTech() {
+    }
+    
+    @Override
+    public Object wrapModel(Object model, Object copyFrom, PropertyBinding[] propArr, FunctionBinding[] funcArr) {
+        return createKO(model, copyFrom, propArr, funcArr, null);
+    }
+
+    final Object createKO(Object model, Object copyFrom, PropertyBinding[] propArr, FunctionBinding[] funcArr, Knockout[] ko) {
+        String[] propNames = new String[propArr.length];
+        Number[] propInfo = new Number[propArr.length];
+        Object[] propValues = new Object[propArr.length];
+        for (int i = 0; i < propNames.length; i++) {
+            propNames[i] = propArr[i].getPropertyName();
+            int info =
+                (propArr[i].isReadOnly() ? 1 : 0) +
+                (propArr[i].isConstant()? 2 : 0);
+            propInfo[i] = info;
+            Object value = propArr[i].getValue();
+            if (value instanceof Enum) {
+                value = value.toString();
+            }
+            propValues[i] = value;
+        }
+        String[] funcNames = new String[funcArr.length];
+        for (int i = 0; i < funcNames.length; i++) {
+            funcNames[i] = funcArr[i].getFunctionName();
+        }
+        Object ret = getJSObject();
+        Knockout newKO = new Knockout(model, ret, propArr, funcArr);
+        if (ko != null) {
+            ko[0] = newKO;
+        }
+        newKO.wrapModel(
+            ret, copyFrom,
+            propNames, propInfo, propValues, funcNames
+        );
+        return ret;
+    }
+    
+    private Object getJSObject() {
+        int len = 64;
+        if (jsObjects != null && jsIndex < (len = jsObjects.length)) {
+            Object ret = jsObjects[jsIndex];
+            jsObjects[jsIndex] = null;
+            jsIndex++;
+            return ret;
+        }
+        jsObjects = Knockout.allocJS(len * 2);
+        jsIndex = 1;
+        Object ret = jsObjects[0];
+        jsObjects[0] = null;
+        return ret;
+    }
+    
+    @Override
+    public Object wrapModel(Object model) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void bind(PropertyBinding b, Object model, Object data) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void valueHasMutated(Object data, String propertyName) {
+        Knockout.cleanUp();
+        Knockout.valueHasMutated(data, propertyName, null, null);
+    }
+    
+    @Override
+    public void valueHasMutated(Object data, String propertyName, Object oldValue, Object newValue) {
+        Knockout.cleanUp();
+        if (newValue instanceof Enum) {
+            newValue = newValue.toString();
+        }
+        Knockout.valueHasMutated(data, propertyName, oldValue, newValue);
+    }
+
+    @Override
+    public void expose(FunctionBinding fb, Object model, Object d) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void applyBindings(Object data) {
+        applyBindings(null, data);
+    }
+    @Override
+    public void applyBindings(String id, Object data) {
+        Object ko = Knockout.applyBindings(id, data);
+        if (ko instanceof Knockout) {
+            ((Knockout)ko).hold();
+        }
+    }
+
+    @Override
+    public Object wrapArray(Object[] arr) {
+        return arr;
+    }
+    
+    @Override
+    public void runSafe(final Runnable r) {
+        LOG.warning("Technology.runSafe has been deprecated. Use BrwsrCtx.execute!");
+        r.run();
+    }    
+
+    @Override
+    public <M> M toModel(Class<M> modelClass, Object data) {
+        return modelClass.cast(Knockout.toModel(data));
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko4j/src/main/java/org/netbeans/html/ko4j/KOTransfer.java
----------------------------------------------------------------------
diff --git a/ko4j/src/main/java/org/netbeans/html/ko4j/KOTransfer.java b/ko4j/src/main/java/org/netbeans/html/ko4j/KOTransfer.java
new file mode 100644
index 0000000..a7ed847
--- /dev/null
+++ b/ko4j/src/main/java/org/netbeans/html/ko4j/KOTransfer.java
@@ -0,0 +1,164 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.ko4j;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.json.spi.JSONCall;
+import org.netbeans.html.json.spi.Transfer;
+
+/** This is an implementation package - just
+ * include its JAR on classpath and use official {@link Context} API
+ * to access the functionality.
+ * <p>
+ *
+ * @author Jaroslav Tulach
+ */
+@Contexts.Id("xhr")
+final class KOTransfer
+implements Transfer {
+    KOTransfer() {
+    }
+
+    @Override
+    public void extract(Object obj, String[] props, Object[] values) {
+        if (obj instanceof JSObjToStr) {
+            obj = ((JSObjToStr)obj).obj;
+        }
+        LoadJSON.extractJSON(obj, props, values);
+    }
+
+    @Override
+    public void loadJSON(final JSONCall call) {
+        if (call.isJSONP()) {
+            String me = LoadJSON.createJSONP(call);
+            LoadJSON.loadJSONP(call.composeURL(me), me);
+        } else {
+            String data = null;
+            if (call.isDoOutput()) {
+                try {
+                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
+                    call.writeData(bos);
+                    data = new String(bos.toByteArray(), "UTF-8");
+                } catch (IOException ex) {
+                    call.notifyError(ex);
+                }
+            }
+            List<String> headerPairs = new ArrayList<String>();
+            String h = call.getHeaders();
+            if (h != null) {
+                int pos = 0;
+                while (pos < h.length()) {
+                    int tagEnd = h.indexOf(':', pos);
+                    if (tagEnd == -1) {
+                        break;
+                    }
+                    int r = h.indexOf('\r', tagEnd);
+                    int n = h.indexOf('\n', tagEnd);
+                    if (r == -1) {
+                        r = h.length();
+                    }
+                    if (n == -1) {
+                        n = h.length();
+                    }
+                    headerPairs.add(h.substring(pos, tagEnd).trim());
+                    headerPairs.add(h.substring(tagEnd + 1, Math.min(r, n)).trim());
+                    pos = Math.max(r, n);
+                }
+            }
+            LoadJSON.loadJSON(call.composeURL(null), call, call.getMethod(), data, headerPairs.toArray());
+        }
+    }
+
+    @Override
+    public Object toJSON(InputStream is) throws IOException {
+        StringBuilder sb = new StringBuilder();
+        InputStreamReader r = new InputStreamReader(is);
+        for (;;) {
+            int ch = r.read();
+            if (ch == -1) {
+                break;
+            }
+            sb.append((char)ch);
+        }
+        return LoadJSON.parse(sb.toString());
+    }
+
+    static void notifySuccess(Object done, Object str, Object data) {
+        Object notifyObj;
+        if (data instanceof Object[]) {
+            Object[] arr = (Object[]) data;
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new JSObjToStr(str, arr[i]);
+            }
+            notifyObj = arr;
+        } else {
+            notifyObj = new JSObjToStr(str, data);
+        }
+        ((JSONCall)done).notifySuccess(notifyObj);
+    }
+
+    static void notifyError(Object done, Object msg) {
+        ((JSONCall)done).notifyError(new Exception(msg.toString()));
+    }
+
+    private static final class JSObjToStr {
+        final String str;
+        final Object obj;
+
+        public JSObjToStr(Object str, Object obj) {
+            this.str = str == null ? "" : str.toString();
+            this.obj = obj;
+        }
+
+        @Override
+        public String toString() {
+            return str;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java
----------------------------------------------------------------------
diff --git a/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java b/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java
new file mode 100644
index 0000000..3479068
--- /dev/null
+++ b/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java
@@ -0,0 +1,277 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.ko4j;
+
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import net.java.html.js.JavaScriptBody;
+import net.java.html.js.JavaScriptResource;
+import net.java.html.json.Model;
+import org.netbeans.html.json.spi.FunctionBinding;
+import org.netbeans.html.json.spi.PropertyBinding;
+
+/** This is an implementation package - just
+ * include its JAR on classpath and use official {@link Context} API
+ * to access the functionality.
+ * <p>
+ * Provides binding between {@link Model models} and knockout.js running
+ * inside a JavaFX WebView. 
+ *
+ * @author Jaroslav Tulach
+ */
+@JavaScriptResource("knockout-3.4.0.js")
+final class Knockout extends WeakReference<Object> {
+    private static final ReferenceQueue<Object> QUEUE = new ReferenceQueue();
+    private static final Set<Knockout> active = Collections.synchronizedSet(new HashSet<Knockout>());
+
+    @JavaScriptBody(args = {"object", "property"}, body = 
+        "var ret;\n" + 
+        "if (property === null) ret = object;\n" + 
+        "else if (object === null) ret = null;\n" + 
+        "else ret = object[property];\n" + 
+        "return ret ? ko.utils.unwrapObservable(ret) : null;"
+    )
+    static Object getProperty(Object object, String property) {
+        return null;
+    }
+    
+    private PropertyBinding[] props;
+    private FunctionBinding[] funcs;
+    private Object js;
+    private Object strong;
+
+    public Knockout(Object model, Object js, PropertyBinding[] props, FunctionBinding[] funcs) {
+        super(model, QUEUE);
+        this.js = js;
+        this.props = new PropertyBinding[props.length];
+        for (int i = 0; i < props.length; i++) {
+            this.props[i] = props[i].weak();
+        }
+        this.funcs = new FunctionBinding[funcs.length];
+        for (int i = 0; i < funcs.length; i++) {
+            this.funcs[i] = funcs[i].weak();
+        }
+        active.add(this);
+    }
+    
+    static void cleanUp() {
+        for (;;) {
+            Knockout ko = (Knockout)QUEUE.poll();
+            if (ko == null) {
+                return;
+            }
+            active.remove(ko);
+            clean(ko.js);
+            ko.js = null;
+            ko.props = null;
+            ko.funcs = null;
+        }
+    }
+    
+    final void hold() {
+        strong = get();
+    }
+    
+    final Object getValue(int index) {
+        return props[index].getValue();
+    }
+    
+    final void setValue(int index, Object v) {
+        if (v instanceof Knockout) {
+            v = ((Knockout)v).get();
+        }
+        props[index].setValue(v);
+    }
+    
+    final void call(int index, Object data, Object ev) {
+        funcs[index].call(data, ev);
+    }
+    
+    @JavaScriptBody(args = { "model", "prop", "oldValue", "newValue" }, 
+        wait4js = false,
+        body =
+          "if (model) {\n"
+        + "  var koProp = model[prop];\n"
+        + "  if (koProp) {\n"
+        + "    var koFire = koProp['valueHasMutated'];\n"
+        + "    if (koFire) {\n"
+        + "      if (oldValue !== null || newValue !== null) {\n"
+        + "        koFire(newValue);\n"
+        + "      } else {\n"
+        + "        koFire();\n"
+        + "      }\n"
+        + "    }\n"
+        + "  }\n"
+        + "}\n"
+    )
+    native static void valueHasMutated(
+        Object model, String prop, Object oldValue, Object newValue
+    );
+
+    @JavaScriptBody(args = { "id", "bindings" }, body = 
+        "var d = window['document'];\n" +
+        "var e = id ? d['getElementById'](id) : d['body'];\n" +
+        "ko['cleanNode'](e);\n" +
+        "ko['applyBindings'](bindings, e);\n" +
+        "return bindings['ko4j'];\n"
+    )
+    native static Object applyBindings(String id, Object bindings);
+    
+    @JavaScriptBody(args = { "cnt" }, body = 
+        "var arr = new Array(cnt);\n" +
+        "for (var i = 0; i < cnt; i++) arr[i] = new Object();\n" +
+        "return arr;\n"
+    )
+    native static Object[] allocJS(int cnt);
+    
+    @JavaScriptBody(
+        javacall = true,
+        keepAlive = false,
+        wait4js = false,
+        args = { "ret", "copyFrom", "propNames", "propInfo", "propValues", "funcNames" },
+        body = 
+          "Object.defineProperty(ret, 'ko4j', { value : this });\n"
+        + "function normalValue(r) {\n"
+        + "  if (r) try { var br = r.valueOf(); } catch (err) {}\n"
+        + "  return br === undefined ? r: br;\n"
+        + "}\n"
+        + "function koComputed(index, name, readOnly, value) {\n"
+        + "  var orig = copyFrom ? copyFrom[name] : null;\n"
+        + "  if (!ko['isObservable'](orig)) {\n"
+        + "    orig = null;\n"
+        + "    var trigger = ko['observable']()['extend']({'notify':'always'});\n"
+        + "  } else {\n"
+        + "    var trigger = orig;\n"
+        + "  }\n"
+        + "  function realGetter() {\n"
+        + "    var self = ret['ko4j'];\n"
+        + "    try {\n"
+        + "      var v = self ? self.@org.netbeans.html.ko4j.Knockout::getValue(I)(index) : null;\n"
+        + "      return v;\n"
+        + "    } catch (e) {\n"
+        + "      alert(\"Cannot call getValue on \" + self + \" prop: \" + name + \" error: \" + e);\n"
+        + "    }\n"
+        + "  }\n"
+        + "  var activeGetter = orig ? orig : function() { return value; };\n"
+        + "  var bnd = {\n"
+        + "    'read': function() {\n"
+        + "      trigger();\n"
+        + "      if (orig) {\n"
+        + "        var r = orig();\n"
+        + "      } else {\n"
+        + "        var r = activeGetter();\n"
+        + "        activeGetter = realGetter;\n"
+        + "      }\n"
+        + "      return normalValue(r);;\n"
+        + "    },\n"
+        + "    'owner': ret\n"
+        + "  };\n"
+        + "  if (!readOnly) {\n"
+        + "    function write(val) {\n"
+        + "      if (orig) orig(val);\n"
+        + "      var self = ret['ko4j'];\n"
+        + "      if (!self) return;\n"
+        + "      var model = val ? val['ko4j'] : null;\n"
+        + "      self.@org.netbeans.html.ko4j.Knockout::setValue(ILjava/lang/Object;)(index, model ? model : val);\n"
+        + "    };\n"
+        + "    bnd['write'] = write;\n"
+        + "    if (orig) {\n"
+        + "      write(orig());\n"
+        + "      orig.subscribe(write);\n"
+        + "    }\n"
+        + "  };\n"
+        + "  var cmpt = ko['computed'](bnd);\n"
+        + "  cmpt['valueHasMutated'] = function(val) {\n"
+        + "    if (arguments.length === 1) activeGetter = function() { return val; };\n"
+        + "    trigger(val);\n"
+        + "  };\n"
+        + "  ret[name] = cmpt;\n"
+        + "}\n"
+        + "for (var i = 0; i < propNames.length; i++) {\n"
+        + "  if ((propInfo[i] & 2) !== 0) {\n"
+        + "    ret[propNames[i]] = normalValue(propValues[i]);\n"
+        + "  } else {\n"
+        + "    koComputed(i, propNames[i], (propInfo[i] & 1) !== 0, propValues[i]);\n"
+        + "  }\n"
+        + "}\n"
+        + "function koExpose(index, name) {\n"
+        + "  ret[name] = function(data, ev) {\n"
+        + "    var self = ret['ko4j'];\n"
+        + "    if (!self) return;\n"
+        + "    self.@org.netbeans.html.ko4j.Knockout::call(ILjava/lang/Object;Ljava/lang/Object;)(index, data, ev);\n"
+        + "  };\n"
+        + "}\n"
+        + "for (var i = 0; i < funcNames.length; i++) {\n"
+        + "  koExpose(i, funcNames[i]);\n"
+        + "}\n"
+        )
+    native void wrapModel(
+        Object ret, Object copyFrom,
+        String[] propNames, Number[] propInfo,
+        Object propValues,
+        String[] funcNames
+    );
+    
+    @JavaScriptBody(args = { "js" }, wait4js = false, body = 
+        "delete js['ko4j'];\n" +
+        "for (var p in js) {\n" +
+        "  delete js[p];\n" +
+        "};\n" +
+        "\n"
+    )
+    private static native void clean(Object js);
+    
+    @JavaScriptBody(args = { "o" }, body = "return o['ko4j'] ? o['ko4j'] : o;")
+    private static native Object toModelImpl(Object wrapper);
+    static Object toModel(Object wrapper) {
+        Object o = toModelImpl(wrapper);
+        if (o instanceof Knockout) {
+            return ((Knockout)o).get();
+        } else {
+            return o;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko4j/src/main/java/org/netbeans/html/ko4j/LoadJSON.java
----------------------------------------------------------------------
diff --git a/ko4j/src/main/java/org/netbeans/html/ko4j/LoadJSON.java b/ko4j/src/main/java/org/netbeans/html/ko4j/LoadJSON.java
new file mode 100644
index 0000000..6957596
--- /dev/null
+++ b/ko4j/src/main/java/org/netbeans/html/ko4j/LoadJSON.java
@@ -0,0 +1,138 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.ko4j;
+
+import net.java.html.js.JavaScriptBody;
+import org.netbeans.html.json.spi.JSONCall;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+final class LoadJSON {
+    private LoadJSON() {}
+
+    static String createJSONP(JSONCall whenDone) {
+        int h = whenDone.hashCode();
+        String name;
+        for (;;) {
+            name = "jsonp" + Integer.toHexString(h);
+            if (defineIfUnused(name, whenDone)) {
+                return name;
+            }
+            h++;
+        }
+    }
+
+    @JavaScriptBody(args = {"name", "done"}, javacall = true, body
+        = "if (window[name]) return false;\n "
+        + "window[name] = function(data) {\n "
+        + "  delete window[name];\n"
+        + "  var el = window.document.getElementById(name);\n"
+        + "  el.parentNode.removeChild(el);\n"
+        + "  done.@org.netbeans.html.json.spi.JSONCall::notifySuccess(Ljava/lang/Object;)(data);\n"
+        + "};\n"
+        + "return true;\n"
+    )
+    private static boolean defineIfUnused(String name, JSONCall done) {
+        return true;
+    }
+
+    @JavaScriptBody(args = {"s"}, body = "return eval('(' + s + ')');")
+    static Object parse(String s) {
+        return s;
+    }
+
+    @JavaScriptBody(args = {"url", "done", "method", "data", "hp"}, javacall = true, body = ""
+        + "var request = new XMLHttpRequest();\n"
+        + "if (!method) method = 'GET';\n"
+        + "request.open(method, url, true);\n"
+        + "request.setRequestHeader('Content-Type', 'application/json; charset=utf-8');\n"
+        + "for (var i = 0; i < hp.length; i += 2) {\n"
+        + "  var h = hp[i];\n"
+        + "  var v = hp[i + 1];\n"
+        + "  request.setRequestHeader(h, v);\n"
+        + "}\n"
+        + "request.onreadystatechange = function() {\n"
+        + "  if (request.readyState !== 4) return;\n"
+        + "  var r = request.response || request.responseText;\n"
+        + "  try {\n"
+        + "    var str = r;\n"
+        + "    if (request.status !== 0)\n"
+        + "      if (request.status < 100 || request.status >= 400) throw request.status + ': ' + request.statusText;"
+        + "    try { r = eval('(' + r + ')'); } catch (ignore) { }"
+        + "    @org.netbeans.html.ko4j.KOTransfer::notifySuccess(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)(done, str, r);\n"
+        + "  } catch (error) {;\n"
+        + "    @org.netbeans.html.ko4j.KOTransfer::notifyError(Ljava/lang/Object;Ljava/lang/Object;)(done, error);\n"
+        + "  }\n"
+        + "};\n"
+        + "request.onerror = function (e) {\n"
+        + "  @org.netbeans.html.ko4j.KOTransfer::notifyError(Ljava/lang/Object;Ljava/lang/Object;)(done, e.type + ' status ' + request.status);\n"
+        + "};\n"
+        + "if (data) request.send(data);\n"
+        + "else request.send();\n"
+    )
+    static void loadJSON(
+        String url, JSONCall done, String method, String data, Object[] headerPairs
+    ) {
+    }
+
+    @JavaScriptBody(args = {"url", "jsonp"}, body
+        = "var scrpt = window.document.createElement('script');\n "
+        + "scrpt.setAttribute('src', url);\n "
+        + "scrpt.setAttribute('id', jsonp);\n "
+        + "scrpt.setAttribute('type', 'text/javascript');\n "
+        + "var body = document.getElementsByTagName('body')[0];\n "
+        + "body.appendChild(scrpt);\n"
+    )
+    static void loadJSONP(String url, String jsonp) {
+
+    }
+
+    static void extractJSON(Object jsonObject, String[] props, Object[] values) {
+        for (int i = 0; i < props.length; i++) {
+            values[i] = Knockout.getProperty(jsonObject, props[i]);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko4j/src/main/java/org/netbeans/html/ko4j/LoadWS.java
----------------------------------------------------------------------
diff --git a/ko4j/src/main/java/org/netbeans/html/ko4j/LoadWS.java b/ko4j/src/main/java/org/netbeans/html/ko4j/LoadWS.java
new file mode 100644
index 0000000..bfd260f
--- /dev/null
+++ b/ko4j/src/main/java/org/netbeans/html/ko4j/LoadWS.java
@@ -0,0 +1,141 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.ko4j;
+
+import net.java.html.js.JavaScriptBody;
+import org.netbeans.html.json.spi.JSONCall;
+
+/** Communication with WebSockets via browser's WebSocket object.
+ *
+ * @author Jaroslav Tulach
+ */
+final class LoadWS {
+    private final Object ws;
+    private final JSONCall call;
+    LoadWS(JSONCall first, String url) {
+        call = first;
+        ws = initWebSocket(this, url);
+        if (ws == null) {
+            first.notifyError(new IllegalArgumentException("Wrong URL: " + url));
+        }
+    }
+    
+    void send(JSONCall call) {
+        push(call);
+    }
+    
+    private synchronized void push(JSONCall call) {
+        send(ws, call.getMessage());
+    }
+
+    void onOpen(Object ev) {
+        if (!call.isDoOutput()) {
+            call.notifySuccess(null);
+        }
+    }
+    
+    
+    @JavaScriptBody(args = { "data" }, body = "try {\n"
+        + "    return eval('(' + data + ')');\n"
+        + "  } catch (error) {;\n"
+        + "    return data;\n"
+        + "  }\n"
+    )
+    private static native Object toJSON(String data);
+    
+    void onMessage(Object ev, String data) {
+        Object json = toJSON(data);
+        call.notifySuccess(json);
+    }
+    
+    void onError(Object ev) {
+        call.notifyError(new Exception(ev.toString()));
+    }
+
+    void onClose(boolean wasClean, int code, String reason) {
+        call.notifyError(null);
+    }
+    
+    @JavaScriptBody(args = { "back", "url" }, javacall = true, body = ""
+        + "if (window.WebSocket) {\n"
+        + "  try {\n"
+        + "    var ws = new window.WebSocket(url);\n"
+        + "    ws.onopen = function(ev) {\n"
+        + "      back.@org.netbeans.html.ko4j.LoadWS::onOpen(Ljava/lang/Object;)(ev);\n"
+        + "    };\n"
+        + "    ws.onmessage = function(ev) {\n"
+        + "      back.@org.netbeans.html.ko4j.LoadWS::onMessage(Ljava/lang/Object;Ljava/lang/String;)(ev, ev.data);\n"
+        + "    };\n"
+        + "    ws.onerror = function(ev) {\n"
+        + "      back.@org.netbeans.html.ko4j.LoadWS::onError(Ljava/lang/Object;)(ev);\n"
+        + "    };\n"
+        + "    ws.onclose = function(ev) {\n"
+        + "      back.@org.netbeans.html.ko4j.LoadWS::onClose(ZILjava/lang/String;)(ev.wasClean, ev.code, ev.reason);\n"
+        + "    };\n"
+        + "    return ws;\n"
+        + "  } catch (ex) {\n"
+        + "    return null;\n"
+        + "  }\n"
+        + "} else {\n"
+        + "  return null;\n"
+        + "}\n"
+    )
+    private static Object initWebSocket(Object back, String url) {
+        return null;
+    }
+    
+
+    @JavaScriptBody(args = { "ws", "msg" }, body = ""
+        + "ws.send(msg);"
+    )
+    private void send(Object ws, String msg) {
+    }
+
+    @JavaScriptBody(args = { "ws" }, body = "ws.close();")
+    private static void close(Object ws) {
+    }
+
+    void close() {
+        close(ws);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko4j/src/test/java/org/netbeans/html/ko4j/DynamicHTTP.java
----------------------------------------------------------------------
diff --git a/ko4j/src/test/java/org/netbeans/html/ko4j/DynamicHTTP.java b/ko4j/src/test/java/org/netbeans/html/ko4j/DynamicHTTP.java
new file mode 100644
index 0000000..fa7e673
--- /dev/null
+++ b/ko4j/src/test/java/org/netbeans/html/ko4j/DynamicHTTP.java
@@ -0,0 +1,261 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.ko4j;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.glassfish.grizzly.PortRange;
+import org.glassfish.grizzly.http.server.HttpHandler;
+import org.glassfish.grizzly.http.server.HttpServer;
+import org.glassfish.grizzly.http.server.NetworkListener;
+import org.glassfish.grizzly.http.server.Request;
+import org.glassfish.grizzly.http.server.Response;
+import org.glassfish.grizzly.http.server.ServerConfiguration;
+import org.glassfish.grizzly.websockets.WebSocket;
+import org.glassfish.grizzly.websockets.WebSocketAddOn;
+import org.glassfish.grizzly.websockets.WebSocketApplication;
+import org.glassfish.grizzly.websockets.WebSocketEngine;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+final class DynamicHTTP extends HttpHandler {
+    private static final Logger LOG = Logger.getLogger(DynamicHTTP.class.getName());
+    private static int resourcesCount;
+    private static List<Resource> resources;
+    private static ServerConfiguration conf;
+    private static HttpServer server;
+
+    private DynamicHTTP() {
+    }
+
+    static URI initServer() throws Exception {
+        server = HttpServer.createSimpleServer(null, new PortRange(8080, 65535));
+        final WebSocketAddOn addon = new WebSocketAddOn();
+        for (NetworkListener listener : server.getListeners()) {
+            listener.registerAddOn(addon);
+        }
+        resources = new ArrayList<Resource>();
+
+        conf = server.getServerConfiguration();
+        final DynamicHTTP dh = new DynamicHTTP();
+
+        conf.addHttpHandler(dh, "/");
+
+        server.start();
+
+        return pageURL("http", server, "/test.html");
+    }
+
+    @Override
+    public void service(Request request, Response response) throws Exception {
+        if ("/test.html".equals(request.getRequestURI())) {
+            response.setContentType("text/html");
+            final InputStream is = DynamicHTTP.class.getResourceAsStream("test.html");
+            copyStream(is, response.getOutputStream(), null);
+            return;
+        }
+        if ("/dynamic".equals(request.getRequestURI())) {
+            String mimeType = request.getParameter("mimeType");
+            List<String> params = new ArrayList<String>();
+            boolean webSocket = false;
+            for (int i = 0;; i++) {
+                String p = request.getParameter("param" + i);
+                if (p == null) {
+                    break;
+                }
+                if ("protocol:ws".equals(p)) {
+                    webSocket = true;
+                    continue;
+                }
+                params.add(p);
+            }
+            final String cnt = request.getParameter("content");
+            String mangle = cnt.replace("%20", " ").replace("%0A", "\n");
+            ByteArrayInputStream is = new ByteArrayInputStream(mangle.getBytes("UTF-8"));
+            URI url;
+            final Resource res = new Resource(is, mimeType, "/dynamic/res" + ++resourcesCount, params.toArray(new String[params.size()]));
+            if (webSocket) {
+                url = registerWebSocket(res);
+            } else {
+                url = registerResource(res);
+            }
+            response.getWriter().write(url.toString());
+            response.getWriter().write("\n");
+            return;
+        }
+
+        for (Resource r : resources) {
+            if (r.httpPath.equals(request.getRequestURI())) {
+                response.setContentType(r.httpType);
+                r.httpContent.reset();
+                String[] params = null;
+                if (r.parameters.length != 0) {
+                    params = new String[r.parameters.length];
+                    for (int i = 0; i < r.parameters.length; i++) {
+                        params[i] = request.getParameter(r.parameters[i]);
+                        if (params[i] == null) {
+                            if ("http.method".equals(r.parameters[i])) {
+                                params[i] = request.getMethod().toString();
+                            } else if ("http.requestBody".equals(r.parameters[i])) {
+                                Reader rdr = request.getReader();
+                                StringBuilder sb = new StringBuilder();
+                                for (;;) {
+                                    int ch = rdr.read();
+                                    if (ch == -1) {
+                                        break;
+                                    }
+                                    sb.append((char) ch);
+                                }
+                                params[i] = sb.toString();
+                            } else if (r.parameters[i].startsWith("http.header.")) {
+                                params[i] = request.getHeader(r.parameters[i].substring(12));
+                            }
+                        }
+                        if (params[i] == null) {
+                            params[i] = "null";
+                        }
+                    }
+                }
+
+                copyStream(r.httpContent, response.getOutputStream(), null, params);
+            }
+        }
+    }
+
+    private URI registerWebSocket(Resource r) {
+        WebSocketEngine.getEngine().register("", r.httpPath, new WS(r));
+        return pageURL("ws", server, r.httpPath);
+    }
+
+    private URI registerResource(Resource r) {
+        if (!resources.contains(r)) {
+            resources.add(r);
+            conf.addHttpHandler(this, r.httpPath);
+        }
+        return pageURL("http", server, r.httpPath);
+    }
+
+    private static URI pageURL(String proto, HttpServer server, final String page) {
+        NetworkListener listener = server.getListeners().iterator().next();
+        int port = listener.getPort();
+        try {
+            return new URI(proto + "://localhost:" + port + page);
+        } catch (URISyntaxException ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+
+    static final class Resource {
+
+        final InputStream httpContent;
+        final String httpType;
+        final String httpPath;
+        final String[] parameters;
+
+        Resource(InputStream httpContent, String httpType, String httpPath,
+            String[] parameters) {
+            httpContent.mark(Integer.MAX_VALUE);
+            this.httpContent = httpContent;
+            this.httpType = httpType;
+            this.httpPath = httpPath;
+            this.parameters = parameters;
+        }
+    }
+
+    static void copyStream(InputStream is, OutputStream os, String baseURL, String... params) throws IOException {
+        for (;;) {
+            int ch = is.read();
+            if (ch == -1) {
+                break;
+            }
+            if (ch == '$' && params.length > 0) {
+                int cnt = is.read() - '0';
+                if (baseURL != null && cnt == 'U' - '0') {
+                    os.write(baseURL.getBytes("UTF-8"));
+                } else {
+                    if (cnt >= 0 && cnt < params.length) {
+                        os.write(params[cnt].getBytes("UTF-8"));
+                    } else {
+                        os.write('$');
+                        os.write(cnt + '0');
+                    }
+                }
+            } else {
+                os.write(ch);
+            }
+        }
+    }
+
+    private static class WS extends WebSocketApplication {
+        private final Resource r;
+
+        private WS(Resource r) {
+            this.r = r;
+        }
+
+        @Override
+        public void onMessage(WebSocket socket, String text) {
+            try {
+                r.httpContent.reset();
+                ByteArrayOutputStream out = new ByteArrayOutputStream();
+                copyStream(r.httpContent, out, null, text);
+                String s = new String(out.toByteArray(), "UTF-8");
+                socket.send(s);
+            } catch (IOException ex) {
+                LOG.log(Level.WARNING, "Error processing message " + text, ex);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko4j/src/test/java/org/netbeans/html/ko4j/InitializeKnockoutTest.java
----------------------------------------------------------------------
diff --git a/ko4j/src/test/java/org/netbeans/html/ko4j/InitializeKnockoutTest.java b/ko4j/src/test/java/org/netbeans/html/ko4j/InitializeKnockoutTest.java
new file mode 100644
index 0000000..c1e2eaf
--- /dev/null
+++ b/ko4j/src/test/java/org/netbeans/html/ko4j/InitializeKnockoutTest.java
@@ -0,0 +1,156 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.ko4j;
+
+import java.net.URL;
+import java.util.concurrent.CountDownLatch;
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.scene.Scene;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.web.WebView;
+import javafx.stage.Stage;
+import net.java.html.BrwsrCtx;
+import net.java.html.boot.fx.FXBrowsers;
+import net.java.html.js.JavaScriptBody;
+import net.java.html.json.Models;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertNotNull;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class InitializeKnockoutTest {
+    public InitializeKnockoutTest() {
+    }
+    
+    @BeforeClass
+    public void initFX() throws Throwable {
+        new Thread("initFX") {
+            @Override
+            public void run() {
+                if (Platform.isFxApplicationThread()) {
+                    new App().start(new Stage());
+                } else {
+                    try {
+                        App.launch(App.class);
+                    } catch (IllegalStateException ex) {
+                        Platform.runLater(this);
+                    }
+                }
+            }
+        }.start();
+        App.CDL.await();
+    }
+
+    @JavaScriptBody(args = {}, body = "return typeof ko !== 'undefined' ? ko : null;")
+    static native Object ko();
+    
+    @Test
+    public void brwsrCtxExecute() throws Throwable {
+        final CountDownLatch init = new CountDownLatch(1);
+        final BrwsrCtx[] ctx = { null };
+        FXBrowsers.runInBrowser(App.webView(), new Runnable() {
+            @Override
+            public void run() {
+                ctx[0] = BrwsrCtx.findDefault(InitializeKnockoutTest.class);
+                init.countDown();
+            }
+        });
+        init.await();
+
+        final CountDownLatch cdl = new CountDownLatch(1);
+        FXBrowsers.runInBrowser(App.webView(), new Runnable() {
+            @Override
+            public void run() {
+                assertNull(ko(), "Knockout isn't yet defined");
+                Models.toRaw(null);
+                assertNotNull(ko(), "After call to toRaw, ko is defined");
+
+                cdl.countDown();
+            }
+        });
+
+        cdl.await();
+    }
+
+    public static class App extends Application {
+        static final CountDownLatch CDL = new CountDownLatch(1);
+        private static BorderPane pane;
+
+        static WebView webView() {
+            try {
+                CDL.await();
+            } catch (InterruptedException ex) {
+                throw new IllegalStateException(ex);
+            }
+            return (WebView)System.getProperties().get("v1");
+        }
+
+        @Override
+        public void start(Stage stage) {
+            pane= new BorderPane();
+            Scene scene = new Scene(pane, 800, 600);
+            stage.setScene(scene);
+            final WebView w1 = new WebView();
+            System.getProperties().put("v1", w1);
+            pane.setCenter(w1);
+
+            
+            URL url = InitializeKnockoutTest.class.getResource("test.html");
+            assertNotNull(url);
+            FXBrowsers.load(w1, url, new Runnable() {
+                @Override
+                public void run() {
+                    CDL.countDown();
+                }
+            });
+
+        }
+        
+        
+    }
+}



[06/24] incubator-netbeans-html4j git commit: [INFRA-15006] Initial donation of HTML/Java NetBeans API. Equivalent to the content of html4j-donation-review.zip donated as part of ApacheNetBeansDonation1.zip with SHA256 being 7f2ca0f61953a190613c9a0fbcc1b

Posted by jt...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/net/java/html/json/PrimitiveArrayTest.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/net/java/html/json/PrimitiveArrayTest.java b/json/src/test/java/net/java/html/json/PrimitiveArrayTest.java
new file mode 100644
index 0000000..9da3ddd
--- /dev/null
+++ b/json/src/test/java/net/java/html/json/PrimitiveArrayTest.java
@@ -0,0 +1,80 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json;
+
+import java.util.Collections;
+import java.util.List;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+@Model(className="ByteArray", properties = {
+    @Property(name = "array", type = byte.class, array = true)
+})
+public class PrimitiveArrayTest {
+    @ComputedProperty static int length(List<Byte> array) {
+        return array.size();
+    }
+
+    @ComputedProperty static List<Integer> lengthAsList(List<Byte> array) {
+        return Collections.nCopies(1, array.size());
+    }
+
+    @ComputedProperty static List<String> lengthTextList(List<Byte> array) {
+        return Collections.nCopies(1, "" + array.size());
+    }
+    
+    @Test public void generatedConstructorWithPrimitiveType() {
+        byte[] arr = new byte[10];
+        arr[3] = 10;
+        ByteArray a = new ByteArray(arr);
+        Assert.assertEquals(a.getArray().size(), 10, "Ten elements");
+        Assert.assertEquals(a.getArray().get(3).byteValue(), 10, "Value ten");
+        Assert.assertEquals(a.getLength(), 10, "Derived property is OK too");
+        Assert.assertEquals(a.getLengthTextList().get(0), "10", "Derived string list property is OK");
+        Assert.assertEquals((int)a.getLengthAsList().get(0), 10, "Derived Integer list property is OK");
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/net/java/html/json/Sex.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/net/java/html/json/Sex.java b/json/src/test/java/net/java/html/json/Sex.java
new file mode 100644
index 0000000..ef00f00
--- /dev/null
+++ b/json/src/test/java/net/java/html/json/Sex.java
@@ -0,0 +1,51 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public enum Sex {
+    MALE, FEMALE;
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/net/java/html/json/TypesTest.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/net/java/html/json/TypesTest.java b/json/src/test/java/net/java/html/json/TypesTest.java
new file mode 100644
index 0000000..693f415
--- /dev/null
+++ b/json/src/test/java/net/java/html/json/TypesTest.java
@@ -0,0 +1,145 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json;
+
+import net.java.html.BrwsrCtx;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+import net.java.html.json.MapModelTest.One;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.json.spi.Technology;
+import org.netbeans.html.json.spi.Transfer;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+@Model(className = "Types", properties = {
+    @Property(name = "intX", type = int.class),
+    @Property(name = "byteX", type = byte.class),
+    @Property(name = "shortX", type = short.class),
+    @Property(name = "longX", type = long.class),
+    @Property(name = "floatX", type = float.class),
+    @Property(name = "doubleX", type = double.class),
+    @Property(name = "charX", type = char.class),
+    @Property(name = "StringX", type = String.class),
+    @Property(name = "boolX", type = boolean.class),
+})
+public class TypesTest {
+    private MapModelTest.MapTechnology t;
+    private BrwsrCtx c;
+
+    @BeforeMethod public void initTechnology() {
+        t = new MapModelTest.MapTechnology();
+        c = Contexts.newBuilder().register(Technology.class, t, 1).
+            register(Transfer.class, t, 1).build();
+    }
+    @Function static void readFromEvent(int intX, 
+        byte byteX, 
+        short shortX, long longX, float floatX, 
+        boolean boolX,
+        char charX,
+        double doubleX,
+        String StringX, Types myModel) {
+        
+        myModel.setIntX(intX);
+        myModel.setDoubleX(doubleX);
+        myModel.setStringX(StringX);
+        
+        myModel.setByteX(byteX);
+        myModel.setShortX(shortX);
+        myModel.setLongX(longX);
+        myModel.setFloatX(floatX);
+        myModel.setBoolX(boolX);
+        myModel.setCharX(charX);
+    }
+    
+    @Test public void canParseEventAttributes() {
+        Types t = Models.bind(new Types(), c);
+        t.setIntX(33);
+        t.setDoubleX(180.5);
+        t.setStringX("Ahoj");
+        t.setCharX('A');
+        t.setByteX((byte)3);
+        t.setShortX((short)10);
+        t.setLongX(66);
+        t.setFloatX(99f);
+        t.setBoolX(true);
+        
+        assertValidJSON(t.toString());
+        
+        Object json = Models.toRaw(t);
+        
+        Types copy = Models.bind(new Types(), c);
+        Map copyMap = (Map) Models.toRaw(copy);
+        One o = (One) copyMap.get("readFromEvent");
+        o.fb.call(null, json);
+        
+        assertEquals(copy.getIntX(), 33);
+        assertEquals(copy.getDoubleX(), 180.5);
+        assertEquals(copy.getStringX(), "Ahoj");
+        assertEquals(copy.getByteX(), (byte)3);
+        assertEquals(copy.getShortX(), (short)10);
+        assertEquals(copy.getLongX(), 66L);
+        assertEquals(copy.getFloatX(), 99f);
+        assertTrue(copy.isBoolX());
+        assertEquals(copy.getCharX(), 'A');
+    }
+    
+    private static void assertValidJSON(String text) {
+        ScriptEngineManager sem = new ScriptEngineManager();
+        ScriptEngine eng = sem.getEngineByMimeType("text/javascript");
+        try {
+            eng.eval("var obj = " + text + ";");
+        } catch (ScriptException ex) {
+            fail("Cannot parse " + text, ex);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/net/java/html/json/UnderscoreTest.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/net/java/html/json/UnderscoreTest.java b/json/src/test/java/net/java/html/json/UnderscoreTest.java
new file mode 100644
index 0000000..884c677
--- /dev/null
+++ b/json/src/test/java/net/java/html/json/UnderscoreTest.java
@@ -0,0 +1,67 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import org.testng.annotations.Test;
+
+
+@Model(className = "UnderscoreModel", targetId="", properties = {
+    @Property(name="__meta", type = MetaModel.class),
+})
+public class UnderscoreTest {
+    @Model(className = "MetaModel", properties = {
+    })
+    static final class MetaCntrl {
+    }
+
+    @Test
+    public void accessMeta() {
+        UnderscoreModel model = new UnderscoreModel();
+        assertNotNull(model);
+        MetaModel meta = new MetaModel();
+        model.set__meta(meta);
+        assertEquals(model.get__meta(), meta);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/net/java/html/json/WebSocketCallTest.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/net/java/html/json/WebSocketCallTest.java b/json/src/test/java/net/java/html/json/WebSocketCallTest.java
new file mode 100644
index 0000000..01139a7
--- /dev/null
+++ b/json/src/test/java/net/java/html/json/WebSocketCallTest.java
@@ -0,0 +1,56 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+@Model(className = "WebSocketCallTestMode", properties = {
+    @Property(name = "nic", type = int.class)
+})
+public class WebSocketCallTest {
+    @OnReceive(method = "WebSocket", data = Person.class, url="{url}")
+    static void wsCall(WebSocketCallTestMode model, Person data) {
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/net/java/html/json/sub/StreetCntrl.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/net/java/html/json/sub/StreetCntrl.java b/json/src/test/java/net/java/html/json/sub/StreetCntrl.java
new file mode 100644
index 0000000..87e13ef
--- /dev/null
+++ b/json/src/test/java/net/java/html/json/sub/StreetCntrl.java
@@ -0,0 +1,53 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json.sub;
+
+import net.java.html.json.Model;
+import net.java.html.json.Property;
+
+@Model(className = "Street", properties = {
+    @Property(name = "name", type = String.class),
+    @Property(name = "id", type = int.class),
+})
+class StreetCntrl {
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/org/netbeans/html/json/impl/BuilderTest.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/org/netbeans/html/json/impl/BuilderTest.java b/json/src/test/java/org/netbeans/html/json/impl/BuilderTest.java
new file mode 100644
index 0000000..55d1dce
--- /dev/null
+++ b/json/src/test/java/org/netbeans/html/json/impl/BuilderTest.java
@@ -0,0 +1,117 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.json.impl;
+
+import java.lang.reflect.Constructor;
+import net.java.html.json.Model;
+import net.java.html.json.Property;
+import static org.testng.Assert.assertEquals;
+import org.testng.annotations.Test;
+
+@Model(className="Builder", builder = "", properties = {
+    @Property(name="bytes", type = byte.class, array = true),
+    @Property(name="chars", type = char.class, array = true),
+    @Property(name="shorts", type = short.class, array = true),
+    @Property(name="ints", type = int.class, array = true),
+    @Property(name="longs", type = long.class, array = true),
+    @Property(name="floats", type = float.class, array = true),
+    @Property(name="doubles", type = double.class, array = true),
+    @Property(name="strings", type = String.class, array = true),
+})
+public class BuilderTest {
+    @Test
+    public void onlyDefaultClassLoader() {
+        Constructor<?>[] arr = Builder.class.getConstructors();
+        assertEquals(arr.length, 1, "One constructor");
+        assertEquals(arr[0].getParameterTypes().length, 0, "No parameters");
+    }
+
+    @Test
+    public void assignBytes() {
+        Builder b = new Builder().bytes((byte)10, (byte)20, (byte)30);
+        assertEquals(b.getBytes().size(), 3);
+        assertEquals(b.getBytes().get(0).byteValue(), (byte)10);
+    }
+    @Test
+    public void assignChars() {
+        Builder b = new Builder().chars((char)10, (char)20, (char)30);
+        assertEquals(b.getChars().size(), 3);
+        assertEquals(b.getChars().get(0).charValue(), 10);
+    }
+    @Test
+    public void assignShort() {
+        Builder b = new Builder().shorts((short)10, (short)20, (short)30);
+        assertEquals(b.getShorts().size(), 3);
+        assertEquals(b.getShorts().get(0).intValue(), 10);
+    }
+    @Test
+    public void assignInts() {
+        Builder b = new Builder().ints(10, 20, 30);
+        assertEquals(b.getInts().size(), 3);
+        assertEquals(b.getInts().get(0).intValue(), 10);
+    }
+    @Test
+    public void assignLongs() {
+        Builder b = new Builder().longs(10, 20, 30);
+        assertEquals(b.getLongs().size(), 3);
+        assertEquals(b.getLongs().get(1).intValue(), 20);
+    }
+    @Test
+    public void assignDouble() {
+        Builder b = new Builder().doubles(10, 20, 30);
+        assertEquals(b.getDoubles().size(), 3);
+        assertEquals(b.getDoubles().get(0), 10.0);
+    }
+    @Test
+    public void assignFloats() {
+        Builder b = new Builder().floats(10, 20, 30);
+        assertEquals(b.getFloats().size(), 3);
+        assertEquals(b.getFloats().get(0), 10.0f);
+    }
+    @Test
+    public void assignStrings() {
+        Builder b = new Builder().strings("A", "AB", "ABC");
+        assertEquals(b.getStrings().size(), 3);
+        assertEquals(b.getStrings().get(1), "AB");
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/org/netbeans/html/json/impl/ConstructorTest.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/org/netbeans/html/json/impl/ConstructorTest.java b/json/src/test/java/org/netbeans/html/json/impl/ConstructorTest.java
new file mode 100644
index 0000000..ffa48c0
--- /dev/null
+++ b/json/src/test/java/org/netbeans/html/json/impl/ConstructorTest.java
@@ -0,0 +1,80 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.json.impl;
+
+import net.java.html.json.Model;
+import net.java.html.json.Property;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertEquals;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+@Model(className="Man", properties={
+    @Property(name = "name", type = String.class),
+    @Property(name = "other", type = Address.class, array = true),
+    @Property(name = "primary", type = Address.class),
+    @Property(name = "childrenNames", type = String.class, array = true)
+})
+public class ConstructorTest {
+    @Model(className = "Address", properties = {
+        @Property(name = "place", type = String.class)
+    })
+    static final class AddressModel {
+    }
+    
+    @Test public void initializedByDefault() {
+        Man m = new Man();
+        assertNotNull(m.getPrimary(), "Single subobjects are initialized");
+    }
+    
+    @Test public void hasRichConstructor() {
+        Man m = new Man("Jarda", new Address("home"), new Address("work"), new Address("hotel"));
+        assertEquals(m.getName(), "Jarda");
+        assertNotNull(m.getPrimary(), "Primary address specified");
+        assertNotNull(m.getPrimary().getPlace(), "home");
+        assertEquals(m.getOther().size(), 2, "Two other addresses");
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/org/netbeans/html/json/impl/DeepChangeTest.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/org/netbeans/html/json/impl/DeepChangeTest.java b/json/src/test/java/org/netbeans/html/json/impl/DeepChangeTest.java
new file mode 100644
index 0000000..5354da3
--- /dev/null
+++ b/json/src/test/java/org/netbeans/html/json/impl/DeepChangeTest.java
@@ -0,0 +1,617 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.json.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import net.java.html.BrwsrCtx;
+import net.java.html.json.ComputedProperty;
+import net.java.html.json.Model;
+import net.java.html.json.Models;
+import net.java.html.json.Property;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.json.spi.FunctionBinding;
+import org.netbeans.html.json.spi.JSONCall;
+import org.netbeans.html.json.spi.PropertyBinding;
+import org.netbeans.html.json.spi.Technology;
+import org.netbeans.html.json.spi.Transfer;
+import static org.testng.Assert.*;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class DeepChangeTest {
+    private MapTechnology t;
+    private BrwsrCtx c;
+
+    @BeforeMethod public void initTechnology() {
+        t = new MapTechnology();
+        c = Contexts.newBuilder().register(Technology.class, t, 1).
+            register(Transfer.class, t, 1).build();
+    }
+    
+    @Model(className = "MyX", targetId = "anythingX", properties = {
+        @Property(name = "one", type = MyY.class),
+        @Property(name = "all", type = MyY.class, array = true)
+    })
+    static class X {
+        @ComputedProperty @Transitive(deep = true)
+        static MyY oneCopy(MyY one) {
+            return Models.bind(one, BrwsrCtx.findDefault(X.class));
+        }
+        @ComputedProperty @Transitive(deep = true) 
+        static String oneName(MyY one) {
+            return one.getValue();
+        }
+        @ComputedProperty
+        static String sndName(MyY one) {
+            if (one == null || one.getValue() == null) {
+                return null;
+            } else {
+                return one.getValue().toUpperCase();
+            }
+        }
+        @ComputedProperty @Transitive(deep = false) 
+        static String noName(MyY one) {
+            if (one == null || one.getValue() == null) {
+                return null;
+            } else {
+                return one.getValue().toUpperCase();
+            }
+        }
+        @ComputedProperty @Transitive(deep = true) 
+        static String thrdName(MyY one) {
+            return "X" + one.getCount();
+        }
+        
+        @ComputedProperty
+        static String allNames(List<MyY> all) {
+            StringBuilder sb = new StringBuilder();
+            for (MyY y : all) {
+                sb.append(y.getValue());
+            }
+            return sb.toString();
+        }
+
+        @ComputedProperty @Transitive(deep = true)
+        static String firstFromNames(List<MyY> all) {
+            for (MyY y : all) {
+                if (y != null && y.getValue() != null) {
+                    return y.getValue();
+                }
+            }
+            return null;
+        }
+    }
+    @Model(className = "MyY", properties = {
+        @Property(name = "value", type = String.class),
+        @Property(name = "count", type = int.class)
+    })
+    static class Y {
+    }
+    @Model(className = "MyOverall", properties = {
+        @Property(name = "x", type = MyX.class)
+    })
+    static class Overall {
+        @ComputedProperty @Transitive(deep = true) 
+        static String valueAccross(MyX x) {
+            return x.getFirstFromNames();
+        }
+    }
+    
+    @Test public void isTransitiveChangeNotifiedProperly() throws Exception {
+        MyX p = Models.bind(
+            new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
+        ), c).applyBindings();
+        
+        Map m = (Map)Models.toRaw(p);
+        Object v = m.get("oneName");
+        assertNotNull(v, "Value should be in the map");
+        assertEquals(v.getClass(), One.class, "It is instance of One");
+        One o = (One)v;
+        assertEquals(o.changes, 0, "No changes so far");
+        assertTrue(o.pb.isReadOnly(), "Derived property");
+        assertEquals(o.get(), "Ahoj");
+
+        p.getOne().setValue("Nazdar");
+        
+        assertEquals(o.get(), "Nazdar");
+        assertEquals(o.changes, 1, "One change so far");
+    }
+    
+    @Test public void isTransitiveChangeInArrayNotifiedProperly() throws Exception {
+        MyX p = Models.bind(
+            new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
+        ), c).applyBindings();
+        
+        Map m = (Map)Models.toRaw(p);
+        Object v = m.get("allNames");
+        assertNotNull(v, "Value should be in the map");
+        assertEquals(v.getClass(), One.class, "It is instance of One");
+        One o = (One)v;
+        assertEquals(o.changes, 0, "No changes so far");
+        assertTrue(o.pb.isReadOnly(), "Derived property");
+        assertEquals(o.get(), "HiHello");
+
+        p.getAll().get(0).setValue("Nazdar");
+        
+        assertEquals(o.get(), "NazdarHello");
+        assertEquals(o.changes, 1, "One change so far");
+    }
+
+    @Test public void changingModelClass() throws Exception {
+        final MyY myY = new MyY("Ahoj", 0);
+        MyX p = Models.bind(
+            new MyX(myY, new MyY("Hi", 333), new MyY("Hello", 999)),
+            c)
+        .applyBindings();
+        MyY realY = p.getOne();
+
+        Map m = (Map)Models.toRaw(p);
+        Object v = m.get("one");
+        assertNotNull(v, "Value should be in the map");
+        assertEquals(v.getClass(), One.class, "It is instance of One");
+        One o = (One)v;
+        assertEquals(o.changes, 0, "No changes so far");
+        assertFalse(o.pb.isReadOnly(), "Normal property");
+        assertEquals(o.get(), myY);
+        assertSame(o.get(), realY);
+
+        final MyY newY = new MyY("Hi", 1);
+        p.setOne(newY);
+
+        assertSame(p.getOne(), newY);
+        assertEquals(o.changes, 1, "One change");
+    }
+
+    @Test public void addingIntoArray() throws Exception {
+        MyX p = Models.bind(
+            new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
+        ), c).applyBindings();
+        
+        Map m = (Map)Models.toRaw(p);
+        Object v = m.get("allNames");
+        assertNotNull(v, "Value should be in the map");
+        assertEquals(v.getClass(), One.class, "It is instance of One");
+        One o = (One)v;
+        assertEquals(o.changes, 0, "No changes so far");
+        assertTrue(o.pb.isReadOnly(), "Derived property");
+        assertEquals(o.get(), "HiHello");
+
+        MyY y = new MyY("Cus", 1);
+        p.getAll().add(y);
+        
+        assertEquals(o.changes, 1, "One change so far");
+        assertEquals(o.get(), "HiHelloCus");
+        
+        y.setValue("Nazdar");
+        
+        assertEquals(o.changes, 2, "2nd change so far");
+        assertEquals(o.get(), "HiHelloNazdar");
+        
+        y.setValue("Zdravim");
+
+        assertEquals(o.changes, 3, "3rd change so far");
+        assertEquals(o.get(), "HiHelloZdravim");
+    }
+    
+    @Test public void firstChangeInArrayNotifiedProperly() throws Exception {
+        MyX p = Models.bind(
+            new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
+        ), c).applyBindings();
+        
+        Map m = (Map)Models.toRaw(p);
+        Object v = m.get("firstFromNames");
+        assertNotNull(v, "Value should be in the map");
+        assertEquals(v.getClass(), One.class, "It is instance of One");
+        One o = (One)v;
+        assertEquals(o.changes, 0, "No changes so far");
+        assertTrue(o.pb.isReadOnly(), "Derived property");
+        assertEquals(o.get(), "Hi");
+
+        p.getAll().get(0).setValue("Nazdar");
+        
+        assertEquals(o.get(), "Nazdar");
+        assertEquals(o.changes, 1, "One change so far");
+    }
+    
+    @Test public void firstChangeInArrayToNull() throws Exception {
+        MyX p = Models.bind(
+            new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
+        ), c).applyBindings();
+        
+        Map m = (Map)Models.toRaw(p);
+        Object v = m.get("firstFromNames");
+        assertNotNull(v, "Value should be in the map");
+        assertEquals(v.getClass(), One.class, "It is instance of One");
+        One o = (One)v;
+        assertEquals(o.changes, 0, "No changes so far");
+        assertTrue(o.pb.isReadOnly(), "Derived property");
+        assertEquals(o.get(), "Hi");
+
+        p.getAll().get(0).setValue(null);
+        
+        assertEquals(o.get(), "Hello");
+        assertEquals(o.changes, 1, "One change so far");
+        
+        p.getAll().get(0).setValue("Nazdar");
+
+        assertEquals(o.get(), "Nazdar");
+        assertEquals(o.changes, 2, "2nd change so far");
+    }
+    
+    @Test public void firstChangeInArrayNotifiedTransitively() throws Exception {
+        MyOverall p = Models.bind(
+            new MyOverall(new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999))
+        ), c);
+        Models.applyBindings(p);
+        
+        Map m = (Map)Models.toRaw(p);
+        Object v = m.get("valueAccross");
+        assertNotNull(v, "Value should be in the map");
+        assertEquals(v.getClass(), One.class, "It is instance of One");
+        One o = (One)v;
+        assertEquals(o.changes, 0, "No changes so far");
+        assertTrue(o.pb.isReadOnly(), "Derived property");
+        assertEquals(o.get(), "Hi");
+
+        p.getX().getAll().get(0).setValue("Nazdar");
+        
+        assertEquals(o.get(), "Nazdar");
+        assertEquals(o.changes, 1, "One change so far");
+    }
+    
+    @Test public void secondChangeInArrayIgnored() throws Exception {
+        MyX p = Models.bind(
+            new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
+        ), c).applyBindings();
+        
+        Map m = (Map)Models.toRaw(p);
+        Object v = m.get("firstFromNames");
+        assertNotNull(v, "Value should be in the map");
+        assertEquals(v.getClass(), One.class, "It is instance of One");
+        One o = (One)v;
+        assertEquals(o.changes, 0, "No changes so far");
+        assertTrue(o.pb.isReadOnly(), "Derived property");
+        assertEquals(o.get(), "Hi");
+
+        p.getAll().get(1).setValue("Nazdar");
+        
+        assertEquals(o.get(), "Hi");
+        assertEquals(o.changes, 0, "No change so far");
+    }
+    
+    @Test public void changeInArraySizeNeedsToBeRecomputed() throws Exception {
+        MyX p = Models.bind(
+            new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
+        ), c).applyBindings();
+        
+        Map m = (Map)Models.toRaw(p);
+        Object v = m.get("firstFromNames");
+        assertNotNull(v, "Value should be in the map");
+        assertEquals(v.getClass(), One.class, "It is instance of One");
+        One o = (One)v;
+        assertEquals(o.changes, 0, "No changes so far");
+        assertTrue(o.pb.isReadOnly(), "Derived property");
+        assertEquals(o.get(), "Hi");
+
+        p.getAll().remove(1);
+        
+        assertEquals(o.get(), "Hi");
+        assertEquals(o.changes, 1, "This required a change");
+    }
+    
+    @Test public void doublePropertyChangeNotified() throws Exception {
+        MyX p = Models.bind(
+            new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
+        ), c).applyBindings();
+        
+        Map m = (Map)Models.toRaw(p);
+        Object v = m.get("oneName");
+        assertNotNull(v, "Value should be in the map");
+        Object v2 = m.get("sndName");
+        assertNotNull(v2, "Value2 should be in the map");
+        One o = (One)v;
+        One o2 = (One)v2;
+        assertEquals(o.changes, 0, "No changes so far");
+        assertEquals(o2.changes, 0, "No changes so far");
+        assertTrue(o.pb.isReadOnly(), "Derived property");
+        assertEquals(o.get(), "Ahoj");
+        assertEquals(o2.get(), "AHOJ");
+
+        p.getOne().setValue("Nazdar");
+        
+        assertEquals(o.get(), "Nazdar");
+        assertEquals(o.changes, 1, "One change so far");
+        assertEquals(o2.changes, 1, "One change so far");
+        assertEquals(o2.get(), "NAZDAR");
+    }
+    
+    @Test public void onlyAffectedPropertyChangeNotified() throws Exception {
+        MyX p = Models.bind(
+            new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
+        ), c).applyBindings();
+        
+        Map m = (Map)Models.toRaw(p);
+        Object v = m.get("oneName");
+        assertNotNull(v, "Value should be in the map");
+        Object v2 = m.get("thrdName");
+        assertNotNull(v2, "Value2 should be in the map");
+        One o = (One)v;
+        One o2 = (One)v2;
+        assertEquals(o.changes, 0, "No changes so far");
+        assertEquals(o2.changes, 0, "No changes so far");
+        assertTrue(o.pb.isReadOnly(), "Derived property");
+        assertEquals(o.get(), "Ahoj");
+        assertEquals(o2.get(), "X0");
+
+        p.getOne().setCount(10);
+        
+        assertEquals(o.get(), "Ahoj");
+        assertEquals(o.changes, 0, "Still no change");
+        assertEquals(o2.changes, 1, "One change so far");
+        assertEquals(o2.get(), "X10");
+    }
+    
+    @Test public void onlyDeepPropsAreNotified() throws Exception {
+        MyX p = Models.bind(
+            new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
+        ), c).applyBindings();
+        
+        Map m = (Map)Models.toRaw(p);
+        Object v = m.get("oneName");
+        assertNotNull(v, "Value should be in the map");
+        Object v2 = m.get("noName");
+        assertNotNull(v2, "Value2 should be in the map");
+        One o = (One)v;
+        One o2 = (One)v2;
+        assertEquals(o.changes, 0, "No changes so far");
+        assertEquals(o2.changes, 0, "No changes so far");
+        assertTrue(o.pb.isReadOnly(), "Derived property");
+        assertEquals(o.get(), "Ahoj");
+        assertEquals(o2.get(), "AHOJ");
+
+        p.getOne().setValue("Nazdar");
+        
+        assertEquals(o.get(), "Nazdar");
+        assertEquals(o.changes, 1, "One change so far");
+        assertEquals(o2.changes, 0, "This change is not noticed");
+        assertEquals(o2.get(), "NAZDAR", "but property value changes when computed");
+    }
+
+    @Test
+    public void mixingContextsIsOK() throws Exception {
+        BrwsrCtx ctx = Contexts.newBuilder().build();
+        final MyY one = Models.bind(new MyY("Ahoj", 0), ctx);
+        MyX p = Models.bind(
+            new MyX(one, new MyY("Hi", 333), new MyY("Hello", 999)), c
+        ).applyBindings();
+
+        Map m = (Map) Models.toRaw(p);
+        Object v = m.get("oneName");
+        assertNotNull(v, "Value should be in the map");
+        One o = (One) v;
+        assertEquals(o.get(), "Ahoj");
+
+        p.getOne().setValue("Nazdar");
+
+        assertEquals(o.get(), "Nazdar");
+        assertEquals(o.changes, 1, "One change so far");
+    }
+    
+    @Test
+    public void rebindReplacesTheInstance() throws Exception {
+        BrwsrCtx ctx = Contexts.newBuilder().build();
+        MyX x = new MyX();
+
+        MyY y = Models.bind(new MyY(), ctx);
+        x.setOne(y);
+
+        assertSame(x.getOne(), y);
+    }
+
+    @Test
+    public void rebindReplacesTheInstanceAndNotifies() throws Exception {
+        BrwsrCtx ctx = Contexts.newBuilder().build();
+        final MyY one = Models.bind(new MyY(), ctx);
+        MyX p = Models.bind(
+            new MyX(one, new MyY("Hi", 333), new MyY("Hello", 999)), c
+        ).applyBindings();
+
+        Map m = (Map) Models.toRaw(p);
+        
+        Object v = m.get("one");
+        assertNotNull(v, "Value should be in the map");
+        One o = (One) v;
+        assertEquals(o.changes, 0, "No changes yet");
+
+        MyY y = Models.bind(new MyY(), ctx);
+        p.setOne(y);
+
+        assertSame(p.getOne(), y);
+        assertSame(o.changes, 1, "One change now");
+    }
+
+    @Test
+    public void mixingWithCloneIsOK() throws Exception {
+        BrwsrCtx ctx = Contexts.newBuilder().build();
+        final MyY one = Models.bind(new MyY("Ahoj", 0), ctx);
+        MyX p = Models.bind(new MyX(one, new MyY("Hi", 333), new MyY("Hello", 999)
+        ), c).applyBindings();
+
+        Map m = (Map) Models.toRaw(p);
+        Object v = m.get("oneCopy");
+        assertNotNull(v, "Value should be in the map");
+        One o = (One) v;
+        assertEquals(((MyY)o.get()).getValue(), "Ahoj");
+
+        p.getOne().setValue("Nazdar");
+
+        assertEquals(((MyY)o.get()).getValue(), "Nazdar");
+        assertEquals(o.changes, 1, "One change so far");
+    }
+
+    static final class One {
+
+        int changes;
+        final PropertyBinding pb;
+        final FunctionBinding fb;
+
+        One(Object m, PropertyBinding pb) throws NoSuchMethodException {
+            this.pb = pb;
+            this.fb = null;
+        }
+
+        One(Object m, FunctionBinding fb) throws NoSuchMethodException {
+            this.pb = null;
+            this.fb = fb;
+        }
+
+        Object get() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+            return pb.getValue();
+        }
+
+        void set(Object v) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+            pb.setValue(v);
+        }
+
+        void assertNoChange(String msg) {
+            assertEquals(changes, 0, msg);
+        }
+
+        void assertChange(String msg) {
+            if (changes == 0) {
+                fail(msg);
+            }
+            changes = 0;
+        }
+    }
+
+    static final class MapTechnology
+            implements Technology<Map<String, One>>, Transfer {
+
+        @Override
+        public Map<String, One> wrapModel(Object model) {
+            return new HashMap<String, One>();
+        }
+
+        @Override
+        public void valueHasMutated(Map<String, One> data, String propertyName) {
+            One p = data.get(propertyName);
+            if (p != null) {
+                p.changes++;
+            }
+        }
+
+        @Override
+        public void bind(PropertyBinding b, Object model, Map<String, One> data) {
+            try {
+                One o = new One(model, b);
+                data.put(b.getPropertyName(), o);
+            } catch (NoSuchMethodException ex) {
+                throw new IllegalStateException(ex);
+            }
+        }
+
+        @Override
+        public void expose(FunctionBinding fb, Object model, Map<String, One> data) {
+            try {
+                data.put(fb.getFunctionName(), new One(model, fb));
+            } catch (NoSuchMethodException ex) {
+                throw new IllegalStateException(ex);
+            }
+        }
+
+        @Override
+        public void applyBindings(Map<String, One> data) {
+        }
+
+        @Override
+        public Object wrapArray(Object[] arr) {
+            return arr;
+        }
+
+        @Override
+        public void extract(Object obj, String[] props, Object[] values) {
+            Map<?, ?> map = obj instanceof Map ? (Map<?, ?>) obj : null;
+            for (int i = 0; i < Math.min(props.length, values.length); i++) {
+                if (map == null) {
+                    values[i] = null;
+                } else {
+                    values[i] = map.get(props[i]);
+                    if (values[i] instanceof One) {
+                        values[i] = ((One) values[i]).pb.getValue();
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void loadJSON(JSONCall call) {
+            call.notifyError(new UnsupportedOperationException());
+        }
+
+        @Override
+        public <M> M toModel(Class<M> modelClass, Object data) {
+            return modelClass.cast(data);
+        }
+
+        @Override
+        public Object toJSON(InputStream is) throws IOException {
+            throw new IOException();
+        }
+
+        @Override
+        public void runSafe(Runnable r) {
+            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/org/netbeans/html/json/impl/EmployeeImpl.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/org/netbeans/html/json/impl/EmployeeImpl.java b/json/src/test/java/org/netbeans/html/json/impl/EmployeeImpl.java
new file mode 100644
index 0000000..c339cde
--- /dev/null
+++ b/json/src/test/java/org/netbeans/html/json/impl/EmployeeImpl.java
@@ -0,0 +1,119 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.json.impl;
+
+import java.util.List;
+import net.java.html.json.Model;
+import net.java.html.json.OnReceive;
+import net.java.html.json.Person;
+import net.java.html.json.Property;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+@Model(className = "Employee", properties = {
+    @Property(name = "person", type = Person.class),
+    @Property(name = "employer", type = Employer.class),
+    @Property(name = "call", type = Call.class)
+})
+public class EmployeeImpl {
+    @OnReceive(url = "some/url")
+    static void changePersonality(Employee e, Person p) {
+        e.setPerson(p);
+    }
+
+    private static void callChangePers(Employee e) {
+        Person per = new Person();
+        e.changePersonalities(10, 3.14, "Ahoj", per);
+        e.updatePersonalities("kuk", new Person(), 1, 2, "3", new Person());
+        e.socketPersonalities("where", null);
+    }
+
+    @OnReceive(url = "some/other/url")
+    static void changePersonalities(Employee e, List<Person> data, int i, double d, String s, Person o) {
+        e.setCall(new Call(i, d, s, o, data.toArray(new Person[0])));
+    }
+
+    @OnReceive(url = "some/other/url", onError = "errorPersonalitiesWithEx")
+    static void changePersonalitiesWithEx(Employee e, List<Person> data, int i, double d, String s, Person o) {
+        e.setCall(new Call(i, d, s, o, data.toArray(new Person[0])));
+    }
+
+    static void errorPersonalitiesWithEx(Employee e, Exception ex) {
+        e.setCall(new Call(-1, -1, null, null));
+    }
+
+    @OnReceive(url = "some/other/url", onError = "errorPersonalitiesWithParam")
+    static void changePersonalitiesWithParam(Employee e, List<Person> data, int i, double d, String s, Person o) {
+        e.setCall(new Call(i, d, s, o, data.toArray(new Person[0])));
+    }
+
+    static void errorPersonalitiesWithParam(Employee e, Exception ex, int i, double d, String s, Person o) {
+        e.setCall(new Call(i, d, s, o));
+    }
+
+    @OnReceive(url = "{url}", method = "PUT", data = Person.class)
+    static void updatePersonalities(Employee e, List<Person> p, int i, double d, String s, Person o) {
+        e.setPerson(p.get(0));
+    }
+
+    @OnReceive(url = "{url}", method = "WebSocket", data = Person.class)
+    static void socketPersonalities(Employee e, List<Person> p) {
+        e.setPerson(p.get(0));
+    }
+    @OnReceive(url = "{url}", method = "WebSocket", data = Person.class)
+    static void socketArrayPersonalities(Employee e, Person[] p) {
+        e.setPerson(p[0]);
+    }
+    
+    @Model(className="Call", properties = {
+        @Property(name = "i", type=int.class),
+        @Property(name = "d", type=double.class),
+        @Property(name = "s", type=String.class),
+        @Property(name = "p", type=Person.class),
+        @Property(name = "data", type=Person.class, array = true)
+    })
+    static class CallModel {
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/org/netbeans/html/json/impl/EmployerTest.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/org/netbeans/html/json/impl/EmployerTest.java b/json/src/test/java/org/netbeans/html/json/impl/EmployerTest.java
new file mode 100644
index 0000000..19b3df9
--- /dev/null
+++ b/json/src/test/java/org/netbeans/html/json/impl/EmployerTest.java
@@ -0,0 +1,65 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.json.impl;
+
+import net.java.html.BrwsrCtx;
+import net.java.html.json.Model;
+import net.java.html.json.Models;
+import net.java.html.json.Property;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+@Model(className = "Employer", properties = {
+    @Property(name = "name", type = String.class)
+})
+public class EmployerTest {
+    @Test public void preLoadsTheClass() {
+        Employer em = Models.fromRaw(BrwsrCtx.EMPTY, Employer.class, this);
+        Assert.assertNotNull(em, "Class loaded");
+        Models.applyBindings(em);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/org/netbeans/html/json/impl/InfinityTest.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/org/netbeans/html/json/impl/InfinityTest.java b/json/src/test/java/org/netbeans/html/json/impl/InfinityTest.java
new file mode 100644
index 0000000..035d55d
--- /dev/null
+++ b/json/src/test/java/org/netbeans/html/json/impl/InfinityTest.java
@@ -0,0 +1,151 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.json.impl;
+
+import net.java.html.json.Model;
+import net.java.html.json.Property;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertSame;
+import org.testng.annotations.Test;
+
+@Model(className = "Infinity", properties = {
+    @Property(name = "next", type = Infinity.class),
+    @Property(name = "address", type = Address.class)
+})
+public class InfinityTest {
+    @Test
+    public void atLeastThousandStepsDeep() {
+        Infinity infinity = new Infinity();
+        int cnt = 0;
+        while (++cnt < 1000) {
+            infinity = infinity.getNext();
+        }
+        assertNotNull(infinity);
+        assertEquals(cnt, 1000);
+    }
+
+    @Test
+    public void afterInitializationRemainsTheSame() {
+        Infinity infinity = new Infinity();
+        Infinity first = infinity.getNext();
+        Infinity second = infinity.getNext();
+        assertSame(first, second);
+    }
+
+    @Test
+    public void nullRemains() {
+        Infinity infinity = new Infinity();
+        infinity.setNext(null);
+        assertNull(infinity.getNext(), "Remains null");
+        assertNull(infinity.getNext(), "Again");
+    }
+
+    @Test
+    public void ownValueRemains() {
+        Infinity infinity = new Infinity();
+        Infinity n = new Infinity();
+        infinity.setNext(n);
+        assertEquals(infinity.getNext(), n, "Remains n");
+        assertEquals(infinity.getNext(), n, "Again n");
+    }
+
+    @Test
+    public void nullRemainsAfterClone() {
+        Infinity infinity = new Infinity();
+        infinity.setNext(null);
+        Infinity clone = infinity.clone();
+        assertNull(clone.getNext(), "Remains null");
+        assertNull(clone.getNext(), "Again");
+        assertEquals(clone.hashCode(), infinity.hashCode(), "Same hashcode");
+    }
+
+    @Test
+    public void ownValueRemainsAfterClone() {
+        Infinity infinity = new Infinity();
+        Infinity n = new Infinity();
+        infinity.setNext(n);
+        Infinity clone = infinity.clone();
+        assertEquals(clone.getNext(), n, "Remains n");
+        assertEquals(clone.getNext(), n, "Again n");
+    }
+
+    @Test
+    public void hashCodeRemainsAfterClone() {
+        Infinity infinity = new Infinity();
+        Infinity n = new Infinity();
+        infinity.setNext(n);
+        Infinity clone = infinity.clone();
+        assertEquals(clone.getNext(), n, "Remains n");
+        assertEquals(clone.getNext(), n, "Again n");
+        assertEquals(clone.hashCode(), infinity.hashCode(), "Same hashcode");
+    }
+
+    @Test
+    public void simpleToStringWithNull() {
+        Infinity infinity = new Infinity();
+        assertNotNull(infinity.getAddress(), "Initialized will be stored as object");
+        assertEquals("{\"next\":null,\"address\":{\"place\":null}}", infinity.toString());
+        infinity.hashCode();
+
+        Infinity second = new Infinity();
+        assertEquals("{\"next\":null,\"address\":null}", second.toString(), "Uninitialized is turned into null");
+
+        second.hashCode();
+    }
+
+    @Test
+    public void toStringWithNullAndClone() {
+        Infinity infinity = new Infinity();
+        infinity.setNext(null);
+        Infinity clone = infinity.clone();
+        assertNull(infinity.getNext(), "Remains null");
+        assertNotNull(infinity.getAddress(), "Address is initialized");
+        assertNull(clone.getNext(), "Clone Remains null");
+        assertNotNull(clone.getAddress(), "Clone Address is initialized");
+        assertEquals(infinity.toString(), clone.toString());
+        assertEquals(clone.hashCode(), infinity.hashCode(), "Same hashcode");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/org/netbeans/html/json/impl/JSONListTest.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/org/netbeans/html/json/impl/JSONListTest.java b/json/src/test/java/org/netbeans/html/json/impl/JSONListTest.java
new file mode 100644
index 0000000..c33df29
--- /dev/null
+++ b/json/src/test/java/org/netbeans/html/json/impl/JSONListTest.java
@@ -0,0 +1,236 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.json.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import net.java.html.BrwsrCtx;
+import net.java.html.json.Model;
+import net.java.html.json.Models;
+import net.java.html.json.People;
+import net.java.html.json.Person;
+import net.java.html.json.Property;
+import net.java.html.json.Sex;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.json.spi.FunctionBinding;
+import org.netbeans.html.json.spi.PropertyBinding;
+import org.netbeans.html.json.spi.Technology;
+import static org.testng.Assert.*;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+@Model(className = "JSNLst", properties = {
+    @Property(name = "names", type = String.class, array = true)
+})
+public class JSONListTest implements Technology<Object> {
+    private boolean replaceArray;
+    private final Map<String,PropertyBinding> bindings = new HashMap<String,PropertyBinding>();
+    private final List<String> changed = new ArrayList<String>();
+    
+    public JSONListTest() {
+    }
+    
+    @BeforeMethod public void clear() {
+        replaceArray = false;
+        changed.clear();
+    }
+
+    @Test public void testConvertorOnAnObject() {
+        BrwsrCtx c = Contexts.newBuilder().register(Technology.class, this, 1).build();
+        
+        Person p = Models.bind(new Person(), c);
+        p.setFirstName("1");
+        p.setLastName("2");
+        p.setSex(Sex.MALE);
+
+        Object real = Models.toRaw(p);
+        assertEquals(this, real, "I am the right model");
+    }
+    
+    @Test public void testConvertorOnAnArray() {
+        BrwsrCtx c = Contexts.newBuilder().register(Technology.class, this, 1).build();
+        
+        Person p = Models.bind(new Person(), c);
+        p.setFirstName("1");
+        p.setLastName("2");
+        p.setSex(Sex.MALE);
+        
+        People people = Models.bind(new People(p), c).applyBindings();
+        assertEquals(people.getInfo().toString(), "[{\"firstName\":\"1\",\"lastName\":\"2\",\"sex\":\"MALE\"}]", "Converted to real JSON");
+        
+        PropertyBinding pb = bindings.get("info");
+        assertNotNull(pb, "Binding for info found");
+        
+        Object real = pb.getValue();
+        assertTrue(real instanceof Object[], "It is an array: " + real);
+        Object[] arr = (Object[])real;
+        assertEquals(arr.length, 1, "Size is one");
+        assertEquals(this, arr[0], "I am the right model");
+    }
+    
+    @Test public void testNicknames() {
+        BrwsrCtx c = Contexts.newBuilder().register(Technology.class, this, 1).build();
+        
+        People people = Models.bind(new People(), c).applyBindings();
+        people.getNicknames().add("One");
+        people.getNicknames().add("Two");
+        
+        PropertyBinding pb = bindings.get("nicknames");
+        assertNotNull(pb, "Binding for info found");
+        
+        Object real = pb.getValue();
+        assertTrue(real instanceof Object[], "It is an array: " + real);
+        Object[] arr = (Object[])real;
+        assertEquals(arr.length, 2, "Length two");
+        assertEquals(arr[0], "One", "Text should be in the model");
+        assertEquals(arr[1], "Two", "2nd text in the model");
+    }
+    
+    @Test public void testConvertorOnAnArrayWithWrapper() {
+        this.replaceArray = true;
+        BrwsrCtx c = Contexts.newBuilder().register(Technology.class, this, 1).build();
+        
+        Person p = Models.bind(new Person(), c);
+        p.setFirstName("1");
+        p.setLastName("2");
+        p.setSex(Sex.MALE);
+        
+        People people = Models.bind(new People(), c).applyBindings();
+        people.getInfo().add(p);
+        
+        Object real = JSON.find(people.getInfo());
+        assertEquals(real, this, "I am the model of the array");
+    }
+
+    @Test public void bindingsOnArray() {
+        this.replaceArray = true;
+        BrwsrCtx c = Contexts.newBuilder().register(Technology.class, this, 1).build();
+        
+        People p = Models.bind(new People(), c).applyBindings();
+        p.getAge().add(30);
+        
+        PropertyBinding pb = bindings.get("age");
+        assertNotNull(pb, "There is a binding for age list");
+        
+        assertEquals(pb.getValue(), this, "I am the model of the array");
+    }
+    
+    @Test public void toStringOnArrayOfStrings() {
+        JSNLst l = new JSNLst("Jarda", "Jirka", "Parda");
+        assertEquals(l.toString(), "{\"names\":[\"Jarda\",\"Jirka\",\"Parda\"]}", "Properly quoted");
+    }
+
+    @Test public void testChangeOnProps() {
+        BrwsrCtx c = Contexts.newBuilder().register(Technology.class, this, 1).build();
+
+        assertTrue(changed.isEmpty());
+        
+        People p = Models.bind(new People(), c).applyBindings();
+        p.getAge().add(42);
+
+        assertEquals(sum(p.getAge()), 42);
+        assertFalse(changed.isEmpty());
+        changed.clear();
+
+        List<Integer> vals = new ArrayList<Integer>();
+        vals.add(12);
+        vals.add(30);
+        ((JSONList)p.getAge()).fastReplace(vals);
+
+        assertEquals(changed.size(), 1, "One change");
+        assertEquals(changed.get(0), "age", "One change");
+        assertEquals(sum(p.getAge()), 42);
+    }
+
+    private static int sum(List<Integer> arr) {
+        int sum = 0;
+        for (Integer i : arr) {
+            sum += i;
+        }
+        return sum;
+    }
+
+    @Override
+    public Object wrapModel(Object model) {
+        return this;
+    }
+
+    @Override
+    public void bind(PropertyBinding b, Object model, Object data) {
+        bindings.put(b.getPropertyName(), b);
+    }
+
+    @Override
+    public void valueHasMutated(Object data, String propertyName) {
+        changed.add(propertyName);
+    }
+
+    @Override
+    public void expose(FunctionBinding fb, Object model, Object d) {
+    }
+
+    @Override
+    public void applyBindings(Object data) {
+    }
+
+    @Override
+    public Object wrapArray(Object[] arr) {
+        return replaceArray ? this : arr;
+    }
+
+    @Override
+    public <M> M toModel(Class<M> modelClass, Object data) {
+        return modelClass.cast(data);
+    }
+
+    @Override
+    public void runSafe(Runnable r) {
+        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/org/netbeans/html/json/impl/JSONTest.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/org/netbeans/html/json/impl/JSONTest.java b/json/src/test/java/org/netbeans/html/json/impl/JSONTest.java
new file mode 100644
index 0000000..e4c7281
--- /dev/null
+++ b/json/src/test/java/org/netbeans/html/json/impl/JSONTest.java
@@ -0,0 +1,84 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.json.impl;
+
+import static org.testng.Assert.*;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class JSONTest {
+    
+    public JSONTest() {
+    }
+
+    @Test public void longToStringValue() {
+        assertEquals(JSON.stringValue(Long.valueOf(1)), "1");
+    }
+    
+    @Test public void booleanIsSortOfNumber() {
+        assertEquals(JSON.numberValue(Boolean.TRUE), Integer.valueOf(1));
+        assertEquals(JSON.numberValue(Boolean.FALSE), Integer.valueOf(0));
+    }
+    
+    @Test public void numberToChar() {
+        assertEquals(JSON.charValue(65), Character.valueOf('A'));
+    }
+    @Test public void booleanToChar() {
+        assertEquals(JSON.charValue(false), Character.valueOf((char)0));
+        assertEquals(JSON.charValue(true), Character.valueOf((char)1));
+    }
+    @Test public void stringToChar() {
+        assertEquals(JSON.charValue("Ahoj"), Character.valueOf('A'));
+    }
+    @Test public void stringToBoolean() {
+        assertEquals(JSON.boolValue("false"), Boolean.FALSE);
+        assertEquals(JSON.boolValue("True"), Boolean.TRUE);
+    }
+    @Test public void numberToBoolean() {
+        assertEquals(JSON.boolValue(0), Boolean.FALSE);
+        assertEquals(JSON.boolValue(1), Boolean.TRUE);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/org/netbeans/html/json/impl/NoPropertiesTest.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/org/netbeans/html/json/impl/NoPropertiesTest.java b/json/src/test/java/org/netbeans/html/json/impl/NoPropertiesTest.java
new file mode 100644
index 0000000..ecf975c
--- /dev/null
+++ b/json/src/test/java/org/netbeans/html/json/impl/NoPropertiesTest.java
@@ -0,0 +1,55 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.json.impl;
+
+import net.java.html.json.Model;
+
+/** Originally could not compile.
+ *
+ * @author Jaroslav Tulach
+ */
+@Model(className="NoProperties", properties = {
+})
+public class NoPropertiesTest {
+    
+}


[14/24] incubator-netbeans-html4j git commit: [INFRA-15006] Initial donation of HTML/Java NetBeans API. Equivalent to the content of html4j-donation-review.zip donated as part of ApacheNetBeansDonation1.zip with SHA256 being 7f2ca0f61953a190613c9a0fbcc1b

Posted by jt...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json-tck/src/main/java/net/java/html/js/tests/JavaScriptBodyTest.java
----------------------------------------------------------------------
diff --git a/json-tck/src/main/java/net/java/html/js/tests/JavaScriptBodyTest.java b/json-tck/src/main/java/net/java/html/js/tests/JavaScriptBodyTest.java
new file mode 100644
index 0000000..1663fa8
--- /dev/null
+++ b/json-tck/src/main/java/net/java/html/js/tests/JavaScriptBodyTest.java
@@ -0,0 +1,548 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.js.tests;
+
+import java.io.StringReader;
+import java.util.Arrays;
+import java.util.concurrent.Callable;
+import org.netbeans.html.boot.spi.Fn;
+import org.netbeans.html.json.tck.KOTest;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class JavaScriptBodyTest {
+    @KOTest public void sumTwoNumbers() {
+        int res = Bodies.sum(5, 3);
+        assertEquals(res, 8, "Expecting 8: " + res);
+    }
+
+    @KOTest public void sumFromCallback() {
+        int res = Bodies.sumJS(5, 3);
+        assertEquals(res, 8, "Expecting 8: " + res);
+    }
+    
+    @KOTest public void accessJsObject() {
+        Object o = Bodies.instance(10);
+        int ten = Bodies.readIntX(o);
+        assertEquals(ten, 10, "Expecting ten: " + ten);
+    }
+
+    @KOTest public void callWithNoReturnType() {
+        Object o = Bodies.instance(10);
+        Bodies.incrementX(o);
+        int ten = Bodies.readIntX(o);
+        assertEquals(ten, 11, "Expecting eleven: " + ten);
+    }
+    
+    @KOTest public void callbackToRunnable() {
+        R run = new R();
+        Bodies.callback(run);
+        assertEquals(run.cnt, 1, "Can call even private implementation classes: " + run.cnt);
+    }
+    
+    private R asyncRun;
+    @KOTest public void asyncCallbackToRunnable() throws InterruptedException {
+        if (asyncRun == null) {
+            asyncRun = new R();
+            Bodies.asyncCallback(asyncRun);
+        }
+        if (asyncRun.cnt == 0) {
+            throw new InterruptedException();
+        }
+        assertEquals(asyncRun.cnt, 1, "Even async callback must arrive once: " + asyncRun.cnt);
+    }
+
+    @KOTest public void asyncCallbackFlushed() throws InterruptedException {
+        R r = new R();
+        for (int i = 0; i < 10; i++) {
+            Bodies.asyncCallback(r);
+        }
+        int fourtyTwo = Bodies.sum(35, 7);
+        assertEquals(r.cnt, 10, "Ten calls: " + r.cnt);
+        assertEquals(fourtyTwo, 42, "Meaning of the world expected: " + fourtyTwo);
+    }
+    
+    @KOTest public void typeOfCharacter() {
+        String charType = Bodies.typeof('a', false);
+        assertEquals("number", charType, "Expecting number type: " + charType);
+    }
+    @KOTest public void typeOfBoolean() {
+        String booleanType = Bodies.typeof(true, false);
+        assertEquals("boolean", booleanType, "Expecting boolean type: " + booleanType);
+    }
+
+    @KOTest public void typeOfPrimitiveBoolean() {
+        String booleanType = Bodies.typeof(true);
+        assertTrue("boolean".equals(booleanType) || "number".equals(booleanType), 
+            "Expecting boolean or at least number type: " + booleanType);
+    }
+
+    @KOTest public void typeOfInteger() {
+        String intType = Bodies.typeof(1, false);
+        assertEquals("number", intType, "Expecting number type: " + intType);
+    }
+
+    @KOTest public void typeOfString() {
+        String strType = Bodies.typeof("Ahoj", false);
+        assertEquals("string", strType, "Expecting string type: " + strType);
+    }
+
+    @KOTest public void typeOfDouble() {
+        String doubleType = Bodies.typeof(0.33, false);
+        assertEquals("number", doubleType, "Expecting number type: " + doubleType);
+    }
+    
+    @KOTest public void typeOfBooleanValueOf() {
+        String booleanType = Bodies.typeof(true, true);
+        assertEquals("boolean", booleanType, "Expecting boolean type: " + booleanType);
+    }
+
+    @KOTest public void typeOfIntegerValueOf() {
+        String intType = Bodies.typeof(1, true);
+        assertEquals("number", intType, "Expecting number type: " + intType);
+    }
+
+    @KOTest public void typeOfStringValueOf() {
+        String strType = Bodies.typeof("Ahoj", true);
+        assertEquals("string", strType, "Expecting string type: " + strType);
+    }
+
+    @KOTest public void typeOfDoubleValueOf() {
+        String doubleType = Bodies.typeof(0.33, true);
+        assertEquals("number", doubleType, "Expecting number type: " + doubleType);
+    }
+
+    @KOTest public void computeInARunnable() {
+        final int[] sum = new int[2];
+        class First implements Runnable {
+            @Override public void run() {
+                sum[0] = Bodies.sum(22, 20);
+                sum[1] = Bodies.sum(32, 10);
+            }
+        }
+        Bodies.callback(new First());
+        assertEquals(sum[0], 42, "Computed OK " + sum[0]);
+        assertEquals(sum[1], 42, "Computed OK too: " + sum[1]);
+    }
+    
+    @KOTest public void doubleCallbackToRunnable() {
+        final R run = new R();
+        final R r2 = new R();
+        class First implements Runnable {
+            @Override public void run() {
+                Bodies.callback(run);
+                Bodies.callback(r2);
+            }
+        }
+        Bodies.callback(new First());
+        assertEquals(run.cnt, 1, "Can call even private implementation classes: " + run.cnt);
+        assertEquals(r2.cnt, 1, "Can call even private implementation classes: " + r2.cnt);
+    }
+    
+    @KOTest public void identity() {
+        Object p = new Object();
+        Object r = Bodies.id(p);
+        assertEquals(r, p, "The object is the same");
+    }
+
+    @KOTest public void encodingString() {
+        Object p = "Ji\n\"Hi\"\nHon";
+        Object r = Bodies.id(p);
+        assertEquals(p, r, "The object is the same: " + p + " != " + r);
+    }
+
+    @KOTest public void encodingBackslashString() {
+        Object p = "{\"firstName\":\"/*\\n * Copyright (c) 2013\",\"lastName\":null,\"sex\":\"MALE\",\"address\":{\"street\":null}}";
+        Object r = Bodies.id(p);
+        assertEquals(p, r, "The object is the same: " + p + " != " + r);
+    }
+
+    @KOTest public void nullIsNull() {
+        Object p = null;
+        Object r = Bodies.id(p);
+        assertEquals(r, p, "The null is the same");
+    }
+    
+    @KOTest public void callbackWithTrueResult() {
+        Callable<Boolean> c = new C(true);
+        String b = Bodies.yesNo(c);
+        assertEquals(b, "yes", "Should return true");
+    }
+
+    @KOTest public void callbackWithFalseResult() {
+        Callable<Boolean> c = new C(false);
+        String b = Bodies.yesNo(c);
+        assertEquals(b, "no", "Should return false");
+    }
+    
+    @KOTest public void callbackWithParameters() throws InterruptedException {
+        Sum s = new Sum();
+        int res = Bodies.sumIndirect(s, 40, 2);
+        assertEquals(res, 42, "Expecting 42");
+    }
+    
+    @KOTest public void selectFromStringJavaArray() {
+        String[] arr = { "Ahoj", "Wo\nrld" };
+        Object res = Bodies.select(arr, 1);
+        assertEquals("Wo\nrld", res, "Expecting World, but was: " + res);
+    }
+
+    @KOTest public void selectFromObjectJavaArray() {
+        Object[] arr = { new Object(), new Object() };
+        Object res = Bodies.select(arr, 1);
+        assertEquals(arr[1], res, "Expecting " + arr[1] + ", but was: " + res);
+    }
+
+    @KOTest public void lengthOfJavaArray() {
+        String[] arr = { "Ahoj", "World" };
+        int res = Bodies.length(arr);
+        assertEquals(res, 2, "Expecting 2, but was: " + res);
+    }
+
+    @KOTest public void isJavaArray() {
+        String[] arr = { "Ahoj", "World" };
+        boolean is = Bodies.isArray(arr);
+        assertTrue(is, "Expecting it to be an array: " + is);
+    }
+
+    @KOTest public void javaArrayInOutIsCopied() {
+        String[] arr = { "Ahoj", "Wo\nrld" };
+        Object res = Bodies.id(arr);
+        assertNotNull(res, "Non-null is returned");
+        assertTrue(res instanceof Object[], "Returned an array: " + res);
+        assertFalse(res instanceof String[], "Not returned a string array: " + res);
+        
+        Object[] ret = (Object[]) res;
+        assertEquals(arr.length, ret.length, "Same length: " + ret.length);
+        assertEquals(arr[0], ret[0], "Same first elem");
+        assertEquals(arr[1], ret[1], "Same 2nd elem");
+    }
+
+    @KOTest public void modifyJavaArrayHasNoEffect() {
+        String[] arr = { "Ah\noj", "World" };
+        String value = Bodies.modify(arr, 0, "H\tello");
+        assertEquals("H\tello", value, "Inside JS the value is changed: " + value);
+        assertEquals("Ah\noj", arr[0], "From a Java point of view it remains: " + arr[0]);
+    }
+
+    @KOTest
+    public void callbackWithArray() {
+        class A implements Callable<String[]> {
+            @Override
+            public String[] call() throws Exception {
+                return new String[] { "He\nllo" };
+            }
+        }
+        Callable<String[]> a = new A();
+        Object b = Bodies.callbackAndPush(a, "Worl\nd!");
+        assertTrue(b instanceof Object[], "Returns an array: " + b);
+        Object[] arr = (Object[]) b;
+        String str = Arrays.toString(arr);
+        assertEquals(arr.length, 2, "Size is two " + str);
+        assertEquals("He\nllo", arr[0], "Hello expected: " + arr[0]);
+        assertEquals("Worl\nd!", arr[1], "World! expected: " + arr[1]);
+    }
+    
+    @KOTest public void sumVector() {
+        double[] arr = { 1.0, 2.0, 3.0 };
+        double res = Bodies.sumVector(arr);
+        assertEquals(6.0, res, "Expecting six: " + res);
+    }
+
+    @KOTest public void sumMatrix() {
+        double[][] arr = { { 1.0 }, { 1.0, 1.0 }, { 1.0, 1.0, 1.0 } };
+        double res = Bodies.sumMatrix(arr);
+        assertEquals(6.0, res, "Expecting six: " + res);
+    }
+
+    @KOTest public void truth() {
+        assertTrue(Bodies.truth(), "True is true");
+    }
+    
+    @KOTest public void factorial2() {
+        assertEquals(new Factorial().factorial(2), 2);
+    }
+    
+    @KOTest public void factorial3() {
+        assertEquals(new Factorial().factorial(3), 6);
+    }
+    
+    @KOTest public void factorial4() {
+        assertEquals(new Factorial().factorial(4), 24);
+    }
+    
+    @KOTest public void factorial5() {
+        assertEquals(new Factorial().factorial(5), 120);
+    }
+    
+    @KOTest public void factorial6() {
+        assertEquals(new Factorial().factorial(6), 720);
+    }
+    
+    @KOTest public void sumArray() {
+        int r = Bodies.sumArr(new Sum());
+        assertEquals(r, 6, "Sum is six: " + r);
+    }
+    
+    @KOTest public void staticCallback() {
+        int r = Bodies.staticCallback();
+        assertEquals(r, 42, "Expecting 42: " + r);
+    }
+
+    @KOTest public void delayCallback() {
+        Object fn = Bodies.delayCallback();
+        Object r = Bodies.invokeFn(fn);
+        assertNotNull(r, "Is not null");
+        assertTrue(r instanceof Number, "Is number " + r);
+        assertEquals(((Number)r).intValue(), 42, "Expecting 42: " + r);
+    }
+    
+    @KOTest public void asyncCallFromAJSCallbackNeedToFinishBeforeReturnToJS() {
+        int r = Bodies.incAsync();
+        assertEquals(r, 42, "Expecting 42: " + r);
+    }
+    
+    @KOTest public void iterateArray() {
+        String[] arr = { "Ahoj", "Hi", "Ciao" };
+        Object[] ret = Bodies.forIn(arr);
+        assertEquals(ret.length, 3, "Three elements returned: " + ret.length);
+        assertNotEquals(ret, arr, "Different arrays");
+        assertEquals(ret[0], "Ahoj", "Expecting Ahoj: " + ret[0]);
+        assertEquals(ret[1], "Hi", "Expecting Hi: " + ret[1]);
+        assertEquals(ret[2], "Ciao", "Expecting Ciao: " + ret[2]);
+    }
+    
+    @KOTest public void primitiveTypes() {
+        String all = Bodies.primitiveTypes(new Sum());
+        assertEquals("Ahojfalse12356.07.0 TheEND", all, "Valid return type: " + all);
+    }
+
+    @KOTest public void returnUnknown() {
+        Object o = Bodies.unknown();
+        assertNull(o, "Unknown is converted to null");
+    }
+
+    @KOTest public void returnUndefinedString() {
+        Object o = Bodies.id("undefined");
+        assertNotNull(o, "String remains string");
+    }
+
+    @KOTest public void returnUnknownArray() {
+        Object[] arr = Bodies.unknownArray();
+        assertEquals(arr.length, 2, "Two elements");
+        assertNull(arr[0], "1st element is null");
+        assertNull(arr[1], "2nd element is null");
+    }
+
+    @KOTest public void callbackKnown() {
+        Sum s = new Sum();
+        boolean nonNull = Bodies.nonNull(s, "x");
+        assertTrue(nonNull, "x property exists");
+    }
+    
+    @KOTest public void callbackUnknown() {
+        Sum s = new Sum();
+        boolean nonNull = Bodies.nonNull(s, "y");
+        assertFalse(nonNull, "y property doesn't exist");
+    }
+
+    @KOTest public void callbackUnknownArray() {
+        Sum s = new Sum();
+        int nullAndUnknown = Bodies.sumNonNull(s);
+        assertEquals(nullAndUnknown, 1, "Only one slot");
+    }
+
+    @KOTest public void problematicString() {
+        String orig = Bodies.problematicString();
+        String js = Bodies.problematicCallback();
+        if (orig.equals(js)) {
+            return;
+        }
+        int len = Math.min(orig.length(), js.length());
+        for (int i = 0; i < len; i++) {
+            if (orig.charAt(i) != js.charAt(i)) {
+                fail("Difference at position " + i + 
+                    "\norig: " +
+                    orig.substring(i - 5, Math.min(i + 10, orig.length())) +
+                    "\n  js: " +
+                    js.substring(i - 5, Math.min(i + 10, js.length())));
+            }
+        }
+        fail("The JS string is different: " + js);
+    }
+
+    @KOTest
+    public void doubleInAnArray() throws Exception {
+        Double val = 2.2;
+        boolean res = Bodies.isInArray(new Object[] { val }, val);
+        assertTrue(res, "Should be in the array");
+    }
+    
+    Later l;
+    @KOTest public void callLater() throws Exception{
+        final Fn.Presenter p = Fn.activePresenter();
+        if (p == null) {
+            return;
+        }
+        if (l == null) {
+            p.loadScript(new StringReader(
+                "if (typeof window === 'undefined') window = {};"
+            ));
+            l = new Later();
+            l.register();
+            p.loadScript(new StringReader(
+                "window.later();"
+            ));
+        }
+        if (l.call != 42) {
+            throw new InterruptedException();
+        }
+        assertEquals(l.call, 42, "Method was called: " + l.call);
+    }
+
+    @KOTest
+    public void globalStringAvailable() throws Exception {
+        assertEquals("HTML/Java", GlobalString.init());
+        assertEquals("HTML/Java", Bodies.readGlobalString());
+    }
+
+    @KOTest
+    public void globalValueInCallbackAvailable() throws Exception {
+        final String[] value = { null, null };
+        Bodies.callback(new Runnable() {
+            @Override
+            public void run() {
+                value[0] = Global2String.init();
+                value[1] = Bodies.readGlobal2String();
+            }
+        });
+        assertEquals(value[0], "NetBeans", "As a returned value from defining method");
+        assertEquals(value[1], "NetBeans", "As read later by different method");
+    }
+    
+    private static class R implements Runnable {
+        int cnt;
+        private final Thread initThread;
+        
+        public R() {
+            initThread = Thread.currentThread();
+        }
+
+        @Override
+        public void run() {
+            assertEquals(initThread, Thread.currentThread(), "Expecting to run in " + initThread + " but running in " + Thread.currentThread());
+            cnt++;
+        }
+    }
+    
+    private static class C implements Callable<Boolean> {
+        private final boolean ret;
+
+        public C(boolean ret) {
+            this.ret = ret;
+        }
+        
+        @Override
+        public Boolean call() throws Exception {
+            return ret;
+        }
+    }
+    static void assertEquals(Object a, Object b, String msg) {
+        if (a == b) {
+            return;
+        }
+        if (a != null && a.equals(b)) {
+            return;
+        }
+        throw new AssertionError(msg);
+    }
+    private static void assertNotEquals(Object a, Object b, String msg) {
+        if (a == null) {
+            if (b == null) {
+                throw new AssertionError(msg);
+            }
+            return;
+        }
+        if (a.equals(b)) {
+            throw new AssertionError(msg);
+        }
+    }
+    static void assertEquals(Object a, Object b) {
+        if (a == b) {
+            return;
+        }
+        if (a != null && a.equals(b)) {
+            return;
+        }
+        throw new AssertionError("Expecting " + b + " but found " + a);
+    }
+    private static void fail(String msg) {
+        throw new AssertionError(msg);
+    }
+
+    private static void assertTrue(boolean c, String msg) {
+        if (!c) {
+            throw new AssertionError(msg);
+        }
+    }
+
+    private static void assertFalse(boolean c, String msg) {
+        if (c) {
+            throw new AssertionError(msg);
+        }
+    }
+
+    private static void assertNull(Object o, String msg) {
+        if (o != null) {
+            throw new AssertionError(msg);
+        }
+    }
+
+    static void assertNotNull(Object o, String msg) {
+        if (o == null) {
+            throw new AssertionError(msg);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json-tck/src/main/java/net/java/html/js/tests/Later.java
----------------------------------------------------------------------
diff --git a/json-tck/src/main/java/net/java/html/js/tests/Later.java b/json-tck/src/main/java/net/java/html/js/tests/Later.java
new file mode 100644
index 0000000..619de61
--- /dev/null
+++ b/json-tck/src/main/java/net/java/html/js/tests/Later.java
@@ -0,0 +1,65 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.js.tests;
+
+import net.java.html.js.JavaScriptBody;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public final class Later {
+    volatile int call;
+
+    @JavaScriptBody(args = {  }, javacall = true, body = 
+        "var self = this;"
+        + "window.later = function() {"
+        + "  self.@net.java.html.js.tests.Later::call(I)(42);"
+       + "};"
+    )
+    native void register();
+    
+    void call(int value) {
+        this.call = value;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json-tck/src/main/java/net/java/html/js/tests/Receiver.java
----------------------------------------------------------------------
diff --git a/json-tck/src/main/java/net/java/html/js/tests/Receiver.java b/json-tck/src/main/java/net/java/html/js/tests/Receiver.java
new file mode 100644
index 0000000..3ed831e
--- /dev/null
+++ b/json-tck/src/main/java/net/java/html/js/tests/Receiver.java
@@ -0,0 +1,80 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.js.tests;
+
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import net.java.html.js.JavaScriptBody;
+
+/**
+ */
+public final class Receiver {
+    private final Object fn;
+    Object value;
+    final Reference<Object> ref;
+
+    public Receiver(Object v) {
+        this.fn = initFn(v);
+        this.ref = new WeakReference<Object>(v);
+        this.value = this;
+    }
+    
+    public void apply() {
+        fnApply(fn, this);
+    }
+    
+    void set(Object v) {
+        value = v;
+    }
+    
+    @JavaScriptBody(args = { "v" }, keepAlive = false, javacall = true, 
+        body = "return function(rec) {\n"
+        + "  rec.@net.java.html.js.tests.Receiver::set(Ljava/lang/Object;)(v);\n"
+        + "};\n")
+    private static native Object initFn(Object v);
+    
+    @JavaScriptBody(args = { "fn", "thiz" }, body =
+        "fn(thiz);"
+    )
+    private static native void fnApply(Object fn, Receiver thiz);
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json-tck/src/main/java/net/java/html/js/tests/Sum.java
----------------------------------------------------------------------
diff --git a/json-tck/src/main/java/net/java/html/js/tests/Sum.java b/json-tck/src/main/java/net/java/html/js/tests/Sum.java
new file mode 100644
index 0000000..d995a7a
--- /dev/null
+++ b/json-tck/src/main/java/net/java/html/js/tests/Sum.java
@@ -0,0 +1,81 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.js.tests;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public final class Sum {
+    public int sum(int a, int b) {
+        return a + b;
+    }
+    
+    public int sum(Object[] arr) {
+        int s = 0;
+        for (int i = 0; i < arr.length; i++) {
+            if (arr[i] instanceof Number) {
+                s += ((Number)arr[i]).intValue();
+            }
+        }
+        return s;
+    }
+
+    public int sumNonNull(Object[] arr) {
+        int s = 0;
+        for (int i = 0; i < arr.length; i++) {
+            if (arr[i] != null) {
+                s++;
+            }
+        }
+        return s;
+    }
+
+    public boolean checkNonNull(Object obj) {
+        return obj != null;
+    }
+    
+    public String all(boolean z, byte b, short s, int i, long l, float f, double d, char ch, String str) {
+        return "Ahoj" + z + b + s + i + l + f + d + ch + str;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json-tck/src/main/java/net/java/html/json/tests/ConvertTypesTest.java
----------------------------------------------------------------------
diff --git a/json-tck/src/main/java/net/java/html/json/tests/ConvertTypesTest.java b/json-tck/src/main/java/net/java/html/json/tests/ConvertTypesTest.java
new file mode 100644
index 0000000..bd558fd
--- /dev/null
+++ b/json-tck/src/main/java/net/java/html/json/tests/ConvertTypesTest.java
@@ -0,0 +1,322 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json.tests;
+
+import java.io.ByteArrayInputStream;
+import java.io.EOFException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import net.java.html.BrwsrCtx;
+import net.java.html.json.Models;
+import org.netbeans.html.json.tck.KOTest;
+import static net.java.html.json.tests.Utils.assertEquals;
+import static net.java.html.json.tests.Utils.assertNull;
+import static net.java.html.json.tests.Utils.assertNotNull;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public final class ConvertTypesTest {
+    private static InputStream createIS(String prefix, boolean includeSex, boolean includeAddress, int array, String suffix)
+    throws UnsupportedEncodingException {
+        StringBuilder sb = new StringBuilder();
+        if (prefix != null) {
+            sb.append(prefix);
+        }
+        int repeat;
+        if (array != -1) {
+            sb.append("[\n");
+            repeat = array;
+        } else {
+            repeat = 1;
+        }
+        for (int i = 0; i < repeat; i++) {
+            sb.append("{ \"firstName\" : \"son\",\n");
+            sb.append("  \"lastName\" : \"dj\" \n");
+            if (includeSex) {
+                sb.append(",  \"sex\" : \"MALE\" \n");
+            }
+            if (includeAddress) {
+                sb.append(",  \"address\" : { \"street\" : \"Schnirchova\" } \n");
+            }
+            sb.append("}\n");
+            if (i < array - 1) {
+                sb.append(",");
+            }
+        }
+        if (array != -1) {
+            sb.append(']');
+        }
+        if (suffix != null) {
+            sb.append(suffix);
+        }
+        return new ByteArrayInputStream(sb.toString().getBytes("UTF-8"));
+    }
+    private static Object createJSON(boolean includeSex) 
+    throws UnsupportedEncodingException {
+        Map<String,Object> map = new HashMap<String,Object>();
+        map.put("firstName", "son");
+        map.put("lastName", "dj");
+        if (includeSex) {
+            map.put("sex", "MALE");
+        }
+        return Utils.createObject(map, ConvertTypesTest.class);
+    }
+    
+    @KOTest
+    public void testConvertToPeople() throws Exception {
+        final Object o = createJSON(true);
+        
+        Person p = Models.fromRaw(newContext(), Person.class, o);
+        
+        assertEquals("son", p.getFirstName(), "First name: " + p.getFirstName());
+        assertEquals("dj", p.getLastName(), "Last name: " + p.getLastName());
+        assertEquals(Sex.MALE, p.getSex(), "Sex: " + p.getSex());
+    }
+
+    @KOTest
+    public void parseConvertToPeople() throws Exception {
+        final BrwsrCtx c = newContext();
+        final InputStream o = createIS(null, true, false, -1, null);
+        
+        Person p = Models.parse(c, Person.class, o);
+        
+        assertEquals("son", p.getFirstName(), "First name: " + p.getFirstName());
+        assertEquals("dj", p.getLastName(), "Last name: " + p.getLastName());
+        assertEquals(Sex.MALE, p.getSex(), "Sex: " + p.getSex());
+    }
+    
+    @KOTest
+    public void parseConvertToPeopleWithAddress() throws Exception {
+        final BrwsrCtx c = newContext();
+        final InputStream o = createIS(null, true, true, -1, null);
+        
+        Person p = Models.parse(c, Person.class, o);
+        
+        assertEquals("son", p.getFirstName(), "First name: " + p.getFirstName());
+        assertEquals("dj", p.getLastName(), "Last name: " + p.getLastName());
+        assertEquals(Sex.MALE, p.getSex(), "Sex: " + p.getSex());
+        assertNotNull(p.getAddress(), "Some address provided");
+        assertEquals(p.getAddress().getStreet(), "Schnirchova", "Is Schnirchova: " + p.getAddress());
+    }
+
+    @KOTest
+    public void parseConvertToPeopleWithAddressIntoAnArray() throws Exception {
+        final BrwsrCtx c = newContext();
+        final InputStream o = createIS(null, true, true, -1, null);
+        
+        List<Person> arr = new ArrayList<Person>();
+        Models.parse(c, Person.class, o, arr);
+        
+        assertEquals(arr.size(), 1, "There is one item in " + arr);
+        
+        Person p = arr.get(0);
+        assertEquals("son", p.getFirstName(), "First name: " + p.getFirstName());
+        assertEquals("dj", p.getLastName(), "Last name: " + p.getLastName());
+        assertEquals(Sex.MALE, p.getSex(), "Sex: " + p.getSex());
+        assertNotNull(p.getAddress() , "Some address provided");
+        assertEquals(p.getAddress().getStreet(), "Schnirchova", "Is Schnirchova: " + p.getAddress());
+    }
+    
+    @KOTest 
+    public void parseNullValue() throws Exception {
+        final BrwsrCtx c = newContext();
+        
+        StringBuilder sb = new StringBuilder();
+        sb.append("{ \"firstName\" : \"son\",\n");
+        sb.append("  \"lastName\" : null } \n");  
+        
+        final ByteArrayInputStream is = new ByteArrayInputStream(sb.toString().getBytes("UTF-8"));
+        Person p = Models.parse(c, Person.class, is);
+
+        assertEquals("son", p.getFirstName(), "First name: " + p.getFirstName());
+        assertNull(p.getLastName(), "Last name: " + p.getLastName());
+    }
+
+    @KOTest 
+    public void parseNullArrayValue() throws Exception {
+        final BrwsrCtx c = newContext();
+        
+        StringBuilder sb = new StringBuilder();
+        sb.append("[ null, { \"firstName\" : \"son\",\n");
+        sb.append("  \"lastName\" : null } ]\n");  
+        
+        final ByteArrayInputStream is = new ByteArrayInputStream(sb.toString().getBytes("UTF-8"));
+        List<Person> arr = new ArrayList<Person>();
+        Models.parse(c, Person.class, is, arr);
+        
+        assertEquals(arr.size(), 2, "There are two items in " + arr);
+        assertNull(arr.get(0), "first is null " + arr);
+        
+        Person p = arr.get(1);
+        assertEquals("son", p.getFirstName(), "First name: " + p.getFirstName());
+        assertNull(p.getLastName(), "Last name: " + p.getLastName());
+    }
+
+    @KOTest
+    public void testConvertToPeopleWithoutSex() throws Exception {
+        final Object o = createJSON(false);
+        
+        Person p = Models.fromRaw(newContext(), Person.class, o);
+        
+        assertEquals("son", p.getFirstName(), "First name: " + p.getFirstName());
+        assertEquals("dj", p.getLastName(), "Last name: " + p.getLastName());
+        assertNull(p.getSex(), "No sex: " + p.getSex());
+    }
+    
+    @KOTest
+    public void parseConvertToPeopleWithoutSex() throws Exception {
+        final BrwsrCtx c = newContext();
+        final InputStream o = createIS(null, false, false, -1, null);
+        Person p = Models.parse(c, Person.class, o);
+        
+        assertEquals("son", p.getFirstName(), "First name: " + p.getFirstName());
+        assertEquals("dj", p.getLastName(), "Last name: " + p.getLastName());
+        assertNull(p.getSex(), "No sex: " + p.getSex());
+    }
+    
+    @KOTest
+    public void parseConvertToPeopleWithAddressOnArray() throws Exception {
+        final BrwsrCtx c = newContext();
+        final InputStream o = createIS(null, true, true, 1, null);
+        
+        Person p = Models.parse(c, Person.class, o);
+        
+        assertEquals("son", p.getFirstName(), "First name: " + p.getFirstName());
+        assertEquals("dj", p.getLastName(), "Last name: " + p.getLastName());
+        assertEquals(Sex.MALE, p.getSex(), "Sex: " + p.getSex());
+        assertNotNull(p.getAddress(), "Some address provided");
+        assertEquals(p.getAddress().getStreet(), "Schnirchova", "Is Schnirchova: " + p.getAddress());
+    }
+
+    @KOTest
+    public void parseConvertToPeopleWithoutSexOnArray() throws Exception {
+        final BrwsrCtx c = newContext();
+        final InputStream o = createIS(null, false, false, 1, null);
+        Person p = Models.parse(c, Person.class, o);
+        
+        assertEquals("son", p.getFirstName(), "First name: " + p.getFirstName());
+        assertEquals("dj", p.getLastName(), "Last name: " + p.getLastName());
+        assertNull(p.getSex(), "No sex: " + p.getSex());
+    }
+
+    @KOTest
+    public void parseFirstElementFromAbiggerArray() throws Exception {
+        final BrwsrCtx c = newContext();
+        final InputStream o = createIS(null, false, false, 5, null);
+        Person p = Models.parse(c, Person.class, o);
+        
+        assertEquals("son", p.getFirstName(), "First name: " + p.getFirstName());
+        assertEquals("dj", p.getLastName(), "Last name: " + p.getLastName());
+        assertNull(p.getSex(), "No sex: " + p.getSex());
+    }
+
+    @KOTest
+    public void parseAllElementFromAbiggerArray() throws Exception {
+        final BrwsrCtx c = newContext();
+        final InputStream o = createIS(null, false, false, 5, null);
+        
+        List<Person> res = new ArrayList<Person>();
+        Models.parse(c, Person.class, o, res);
+        
+        assertEquals(res.size(), 5, "Five elements found" + res);
+        
+        for (Person p : res) {
+            assertEquals("son", p.getFirstName(), "First name: " + p.getFirstName());
+            assertEquals("dj", p.getLastName(), "Last name: " + p.getLastName());
+            assertNull(p.getSex(), "No sex: " + p.getSex());
+        }
+    }
+
+    @KOTest
+    public void parseFiveElementsAsAnArray() throws Exception {
+        doParseInnerArray(5, 5);
+    }
+
+    @KOTest
+    public void parseInnerElementAsAnArray() throws Exception {
+        doParseInnerArray(-1, 1);
+    }
+    private void doParseInnerArray(int array, int expect) throws Exception {
+        final BrwsrCtx c = newContext();
+        final InputStream o = createIS("{ \"info\" : ", false, false, array, "}");
+
+        List<People> res = new ArrayList<People>();
+        Models.parse(c, People.class, o, res);
+
+        assertEquals(res.size(), 1, "One people" + res);
+
+        int cnt = 0;
+        for (Person p : res.get(0).getInfo()) {
+            assertEquals("son", p.getFirstName(), "First name: " + p.getFirstName());
+            assertEquals("dj", p.getLastName(), "Last name: " + p.getLastName());
+            assertNull(p.getSex(), "No sex: " + p.getSex());
+            cnt++;
+        }
+
+        assertEquals(cnt, expect, "Person found in info");
+    }
+    
+    @KOTest
+    public void parseOnEmptyArray() throws Exception {
+        final BrwsrCtx c = newContext();
+        final InputStream o = createIS(null, false, false, 0, null);
+        
+        try {
+            Models.parse(c, Person.class, o);
+        } catch (EOFException ex) {
+            // OK
+            return;
+        }
+        throw new IllegalStateException("Should throw end of file exception, as the array is empty");
+    }
+    
+    private static BrwsrCtx newContext() {
+        return Utils.newContext(ConvertTypesTest.class);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json-tck/src/main/java/net/java/html/json/tests/GCKnockoutTest.java
----------------------------------------------------------------------
diff --git a/json-tck/src/main/java/net/java/html/json/tests/GCKnockoutTest.java b/json-tck/src/main/java/net/java/html/json/tests/GCKnockoutTest.java
new file mode 100644
index 0000000..1cb2b7c
--- /dev/null
+++ b/json-tck/src/main/java/net/java/html/json/tests/GCKnockoutTest.java
@@ -0,0 +1,139 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json.tests;
+
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import net.java.html.BrwsrCtx;
+import net.java.html.json.Model;
+import net.java.html.json.Models;
+import net.java.html.json.Property;
+import org.netbeans.html.json.tck.KOTest;
+import static net.java.html.json.tests.Utils.assertEquals;
+
+@Model(className = "GC", properties = {
+    @Property(name = "all", type = Fullname.class, array = true)
+})
+public class GCKnockoutTest {
+    @Model(className = "Fullname", properties = {
+        @Property(name = "firstName", type = String.class),
+        @Property(name = "lastName", type = String.class)
+    })
+    static class FullnameCntrl {
+    }
+    
+    @KOTest public void noLongerNeededArrayElementsCanDisappear() throws Exception {
+        BrwsrCtx ctx = Utils.newContext(GCKnockoutTest.class);
+        Object exp = Utils.exposeHTML(GCKnockoutTest.class,
+            "<ul id='ul' data-bind='foreach: all'>\n"
+            + "  <li data-bind='text: firstName'/>\n"
+            + "</ul>\n"
+        );
+        try {
+            GC m = Models.bind(new GC(), ctx);
+            m.getAll().add(new Fullname("Jarda", "Tulach"));
+            Models.applyBindings(m);
+
+            int cnt = Utils.countChildren(GCKnockoutTest.class, "ul");
+            assertEquals(cnt, 1, "One child, but was " + cnt);
+
+            m.getAll().add(new Fullname("HTML", "Java"));
+
+            cnt = Utils.countChildren(GCKnockoutTest.class, "ul");
+            assertEquals(cnt, 2, "Now two " + cnt);
+
+            Fullname removed = m.getAll().get(0);
+            m.getAll().remove(0);
+
+            cnt = Utils.countChildren(GCKnockoutTest.class, "ul");
+            assertEquals(cnt, 1, "Again One " + cnt);
+
+            Reference<?> ref = new WeakReference<Object>(removed);
+            removed = null;
+            assertGC(ref, "Can removed object disappear?");
+            
+            ref = new WeakReference<Object>(m);
+            m = null;
+            assertNotGC(ref, "Root model cannot GC");
+        } finally {
+            Utils.exposeHTML(GCKnockoutTest.class, "");
+        }
+        
+    }
+    
+    private void assertGC(Reference<?> ref, String msg) throws Exception {
+        for (int i = 0; i < 100; i++) {
+            if (ref.get() == null) {
+                return;
+            }
+            String gc = "var max = arguments[0];\n"
+                    +  "var arr = [];\n"
+                    + "for (var i = 0; i < max; i++) {\n"
+                    + "  arr.push(i);\n"
+                    + "}\n"
+                    + "return arr.length;";
+            Object cnt = Utils.executeScript(GCKnockoutTest.class, gc, Math.pow(2.0, i));
+            System.gc();
+            System.runFinalization();
+        }
+        throw new OutOfMemoryError(msg);
+    }
+    
+    private void assertNotGC(Reference<?> ref, String msg) throws Exception {
+        for (int i = 0; i < 10; i++) {
+            if (ref.get() == null) {
+                throw new IllegalStateException(msg);
+            }
+            String gc = "var max = arguments[0];\n"
+                    +  "var arr = [];\n"
+                    + "for (var i = 0; i < max; i++) {\n"
+                    + "  arr.push(i);\n"
+                    + "}\n"
+                    + "return arr.length;";
+            Object cnt = Utils.executeScript(GCKnockoutTest.class, gc, Math.pow(2.0, i));
+            System.gc();
+            System.runFinalization();
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json-tck/src/main/java/net/java/html/json/tests/JSONTest.java
----------------------------------------------------------------------
diff --git a/json-tck/src/main/java/net/java/html/json/tests/JSONTest.java b/json-tck/src/main/java/net/java/html/json/tests/JSONTest.java
new file mode 100644
index 0000000..0021e2b
--- /dev/null
+++ b/json-tck/src/main/java/net/java/html/json/tests/JSONTest.java
@@ -0,0 +1,637 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json.tests;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import net.java.html.BrwsrCtx;
+import net.java.html.json.Model;
+import net.java.html.json.ModelOperation;
+import net.java.html.json.Models;
+import net.java.html.json.OnReceive;
+import net.java.html.json.Property;
+import static net.java.html.json.tests.Utils.assertEquals;
+import static net.java.html.json.tests.Utils.assertNotNull;
+import static net.java.html.json.tests.Utils.assertNull;
+import static net.java.html.json.tests.Utils.assertTrue;
+import org.netbeans.html.json.tck.KOTest;
+
+/** Need to verify that models produce reasonable JSON objects.
+ *
+ * @author Jaroslav Tulach
+ */
+@Model(className = "JSONik", targetId = "", properties = {
+    @Property(name = "fetched", type = Person.class),
+    @Property(name = "fetchedCount", type = int.class),
+    @Property(name = "fetchedResponse", type = String.class),
+    @Property(name = "fetchedSex", type = Sex.class, array = true)
+})
+public final class JSONTest {
+    private JSONik js;
+    private Integer orig;
+    private String url;
+
+    @ModelOperation static void assignFetched(JSONik m, Person p) {
+        m.setFetched(p);
+    }
+    private BrwsrCtx ctx;
+
+    @KOTest public void toJSONInABrowser() throws Throwable {
+        Person p = Models.bind(new Person(), newContext());
+        p.setSex(Sex.MALE);
+        p.setFirstName("Jarda");
+        p.setLastName("Tulach");
+
+        Object json;
+        try {
+            json = parseJSON(p.toString());
+        } catch (Throwable ex) {
+            throw new IllegalStateException("Can't parse " + p).initCause(ex);
+        }
+
+        Person p2 = Models.fromRaw(newContext(), Person.class, json);
+
+        assertEquals(p2.getFirstName(), p.getFirstName(),
+            "Should be the same: " + p.getFirstName() + " != " + p2.getFirstName());
+    }
+
+    @KOTest public void toJSONWithEscapeCharactersInABrowser() throws Throwable {
+        Person p = Models.bind(new Person(), newContext());
+        p.setSex(Sex.MALE);
+        p.setFirstName("/*\n * Copyright (c) 2013");
+
+
+        final String txt = p.toString();
+        Object json;
+        try {
+            json = parseJSON(txt);
+        } catch (Throwable ex) {
+            throw new IllegalStateException("Can't parse " + txt).initCause(ex);
+        }
+
+        Person p2 = Models.fromRaw(newContext(), Person.class, json);
+
+        assertEquals(p2.getFirstName(), p.getFirstName(),
+            "Should be the same: " + p.getFirstName() + " != " + p2.getFirstName());
+    }
+
+    @KOTest public void toJSONWithDoubleSlashInABrowser() throws Throwable {
+        Person p = Models.bind(new Person(), newContext());
+        p.setSex(Sex.MALE);
+        p.setFirstName("/*\\n * Copyright (c) 2013");
+
+
+        final String txt = p.toString();
+        Object json;
+        try {
+            json = parseJSON(txt);
+        } catch (Throwable ex) {
+            throw new IllegalStateException("Can't parse " + txt).initCause(ex);
+        }
+
+        Person p2 = Models.fromRaw(newContext(), Person.class, json);
+
+        assertEquals(p2.getFirstName(), p.getFirstName(),
+            "Should be the same: " + p.getFirstName() + " != " + p2.getFirstName());
+    }
+
+    @KOTest public void toJSONWithApostrophInABrowser() throws Throwable {
+        Person p = Models.bind(new Person(), newContext());
+        p.setSex(Sex.MALE);
+        p.setFirstName("Jimmy 'Jim' Rambo");
+
+
+        final String txt = p.toString();
+        Object json;
+        try {
+            json = parseJSON(txt);
+        } catch (Throwable ex) {
+            throw new IllegalStateException("Can't parse " + txt).initCause(ex);
+        }
+
+        Person p2 = Models.fromRaw(newContext(), Person.class, json);
+
+        assertEquals(p2.getFirstName(), p.getFirstName(),
+            "Should be the same: " + p.getFirstName() + " != " + p2.getFirstName());
+    }
+
+    private static BrwsrCtx onCallback;
+
+    @OnReceive(url="{url}")
+    static void fetch(JSONik model, Person p) {
+        model.setFetched(p);
+        onCallback = BrwsrCtx.findDefault(model.getClass());
+    }
+
+    @OnReceive(url="{url}")
+    static void fetchPlain(JSONik model, String p) {
+        onCallback = BrwsrCtx.findDefault(model.getClass());
+        model.setFetchedResponse(p);
+    }
+
+    @OnReceive(url="{url}", onError = "setMessage")
+    static void fetchArray(JSONik model, Person[] p) {
+        model.setFetchedCount(p.length);
+        model.setFetched(p[0]);
+        onCallback = BrwsrCtx.findDefault(model.getClass());
+    }
+
+    static void setMessage(JSONik m, Exception t) {
+        assertNotNull(t, "Exception provided");
+        m.setFetchedResponse("Exception");
+    }
+
+    @OnReceive(url="{url}")
+    static void fetchPeople(JSONik model, People p) {
+        final int size = p.getInfo().size();
+        if (size > 0) {
+            model.setFetched(p.getInfo().get(0));
+        }
+        model.setFetchedCount(size);
+    }
+
+    @OnReceive(url="{url}")
+    static void fetchPeopleAge(JSONik model, People p) {
+        int sum = 0;
+        for (int a : p.getAge()) {
+            sum += a;
+        }
+        model.setFetchedCount(sum);
+    }
+
+    @KOTest public void loadAndParseJSON() throws InterruptedException {
+        if (js == null) {
+            url = Utils.prepareURL(
+                JSONTest.class, "{'firstName': 'Sitar', 'sex': 'MALE'}",
+                "application/json"
+            );
+            js = Models.bind(new JSONik(), ctx = newContext());
+            js.applyBindings();
+
+            js.setFetched(null);
+            js.fetch(url);
+        }
+
+        Person p = js.getFetched();
+        if (p == null) {
+            throw new InterruptedException();
+        }
+
+        assertEquals("Sitar", p.getFirstName(), "Expecting Sitar: " + p.getFirstName());
+        assertEquals(Sex.MALE, p.getSex(), "Expecting MALE: " + p.getSex());
+
+        assertEquals(ctx, onCallback, "Context is the same");
+    }
+
+    @KOTest public void loadAndParsePlainText() throws Exception {
+        if (js == null) {
+            url = Utils.prepareURL(
+                JSONTest.class, "{'firstName': 'Sitar', 'sex': 'MALE'}",
+                "text/plain"
+            );
+            js = Models.bind(new JSONik(), ctx = newContext());
+            js.applyBindings();
+
+            js.setFetched(null);
+            js.fetchPlain(url);
+        }
+
+        String s = js.getFetchedResponse();
+        if (s == null) {
+            throw new InterruptedException();
+        }
+
+        assertTrue(s.contains("Sitar"), "The text contains Sitar value: " + s);
+        assertTrue(s.contains("MALE"), "The text contains MALE value: " + s);
+
+        Person p = Models.parse(ctx, Person.class, new ByteArrayInputStream(s.getBytes()));
+
+        assertEquals("Sitar", p.getFirstName(), "Expecting Sitar: " + p.getFirstName());
+        assertEquals(Sex.MALE, p.getSex(), "Expecting MALE: " + p.getSex());
+
+        assertEquals(ctx, onCallback, "Same context");
+    }
+
+    @KOTest public void loadAndParsePlainTextOnArray() throws Exception {
+        if (js == null) {
+            url = Utils.prepareURL(
+                JSONTest.class, "[ {'firstName': 'Sitar', 'sex': 'MALE'} ]",
+                "text/plain"
+            );
+            js = Models.bind(new JSONik(), ctx = newContext());
+            js.applyBindings();
+
+            js.setFetched(null);
+            js.fetchPlain(url);
+        }
+
+        String s = js.getFetchedResponse();
+        if (s == null) {
+            throw new InterruptedException();
+        }
+
+        assertTrue(s.contains("Sitar"), "The text contains Sitar value: " + s);
+        assertTrue(s.contains("MALE"), "The text contains MALE value: " + s);
+
+        Person p = Models.parse(ctx, Person.class, new ByteArrayInputStream(s.getBytes()));
+
+        assertEquals("Sitar", p.getFirstName(), "Expecting Sitar: " + p.getFirstName());
+        assertEquals(Sex.MALE, p.getSex(), "Expecting MALE: " + p.getSex());
+
+        assertEquals(ctx, onCallback, "Same context");
+    }
+
+    @OnReceive(url="{url}?callme={me}", jsonp = "me")
+    static void fetchViaJSONP(JSONik model, Person p) {
+        model.setFetched(p);
+    }
+
+    @KOTest public void loadAndParseJSONP() throws InterruptedException, Exception {
+        if (js == null) {
+            url = Utils.prepareURL(
+                JSONTest.class, "$0({'firstName': 'Mitar', 'sex': 'MALE'})",
+                "application/javascript",
+                "callme"
+            );
+            orig = scriptElements();
+            assertTrue(orig > 0, "There should be some scripts on the page");
+
+            js = Models.bind(new JSONik(), newContext());
+            js.applyBindings();
+
+            js.setFetched(null);
+            js.fetchViaJSONP(url);
+        }
+
+        Person p = js.getFetched();
+        if (p == null) {
+            throw new InterruptedException();
+        }
+
+        assertEquals("Mitar", p.getFirstName(), "Unexpected: " + p.getFirstName());
+        assertEquals(Sex.MALE, p.getSex(), "Expecting MALE: " + p.getSex());
+
+        int now = scriptElements();
+
+        assertEquals(orig, now, "The set of elements is unchanged. Delta: " + (now - orig));
+    }
+
+
+
+    @OnReceive(url="{url}", method = "PUT", data = Person.class)
+    static void putPerson(JSONik model, String reply) {
+        model.setFetchedCount(1);
+        model.setFetchedResponse(reply);
+    }
+
+    @KOTest public void putPeopleUsesRightMethod() throws InterruptedException, Exception {
+        if (js == null) {
+            url = Utils.prepareURL(
+                JSONTest.class, "$0\n$1",
+                "text/plain",
+                "http.method", "http.requestBody"
+            );
+            orig = scriptElements();
+            assertTrue(orig > 0, "There should be some scripts on the page");
+
+            js = Models.bind(new JSONik(), newContext());
+            js.applyBindings();
+
+            Person p = Models.bind(new Person(), BrwsrCtx.EMPTY);
+            p.setFirstName("Jarda");
+            js.putPerson(url, p);
+        }
+
+        int cnt = js.getFetchedCount();
+        if (cnt == 0) {
+            throw new InterruptedException();
+        }
+        String res = js.getFetchedResponse();
+        int line = res.indexOf('\n');
+        String msg;
+        if (line >= 0) {
+            msg = res.substring(line + 1);
+            res = res.substring(0, line);
+        } else {
+            msg = res;
+        }
+
+        assertEquals("PUT", res, "Server was queried with PUT method: " + js.getFetchedResponse());
+
+        assertTrue(msg.contains("Jarda"), "Data transferred to the server: " + msg);
+    }
+
+    private static int scriptElements() throws Exception {
+        return ((Number)Utils.executeScript(
+            JSONTest.class,
+            "return window.document.getElementsByTagName('script').length;")).intValue();
+    }
+
+    private static Object parseJSON(String s) throws Exception {
+        return Utils.executeScript(
+            JSONTest.class,
+            "return window.JSON.parse(arguments[0]);", s);
+    }
+
+    @KOTest public void loadAndParseJSONSentToArray() throws InterruptedException {
+        if (js == null) {
+            url = Utils.prepareURL(
+                JSONTest.class, "{'firstName': 'Sitar', 'sex': 'MALE'}",
+                "application/json"
+            );
+
+            js = Models.bind(new JSONik(), newContext());
+            js.applyBindings();
+
+            js.setFetched(null);
+            js.fetchArray(url);
+        }
+
+        Person p = js.getFetched();
+        if (p == null) {
+            throw new InterruptedException();
+        }
+
+        assertEquals("Sitar", p.getFirstName(), "Expecting Sitar: " + p.getFirstName());
+        assertEquals(Sex.MALE, p.getSex(), "Expecting MALE: " + p.getSex());
+    }
+
+    @KOTest public void loadAndParseJSONArraySingle() throws InterruptedException {
+        if (js == null) {
+            url = Utils.prepareURL(
+                JSONTest.class, "[{'firstName': 'Gitar', 'sex': 'FEMALE'}]",
+                "application/json"
+            );
+            js = Models.bind(new JSONik(), newContext());
+            js.applyBindings();
+
+            js.setFetched(null);
+            js.fetch(url);
+        }
+
+        Person p = js.getFetched();
+        if (p == null) {
+            throw new InterruptedException();
+        }
+
+        assertEquals("Gitar", p.getFirstName(), "Expecting Gitar: " + p.getFirstName());
+        assertEquals(Sex.FEMALE, p.getSex(), "Expecting FEMALE: " + p.getSex());
+    }
+
+    @KOTest public void loadAndParseArrayInPeople() throws InterruptedException {
+        if (js == null) {
+            url = Utils.prepareURL(
+                JSONTest.class, "{'info':[{'firstName': 'Gitar', 'sex': 'FEMALE'}]}",
+                "application/json"
+            );
+            js = Models.bind(new JSONik(), newContext());
+            js.applyBindings();
+
+            js.fetchPeople(url);
+        }
+
+        if (0 == js.getFetchedCount()) {
+            throw new InterruptedException();
+        }
+
+        assertEquals(js.getFetchedCount(), 1, "One person loaded: " + js.getFetchedCount());
+
+        Person p = js.getFetched();
+
+        assertNotNull(p, "We should get our person back: " + p);
+        assertEquals("Gitar", p.getFirstName(), "Expecting Gitar: " + p.getFirstName());
+        assertEquals(Sex.FEMALE, p.getSex(), "Expecting FEMALE: " + p.getSex());
+    }
+
+    @KOTest public void loadAndParseArrayInPeopleWithHeaders() throws InterruptedException {
+        if (js == null) {
+            url = Utils.prepareURL(
+                JSONTest.class, "{'info':[{'firstName': '$0$1$2$3$4', 'sex': 'FEMALE'}]}",
+                "application/json",
+                "http.header.Easy",
+                "http.header.H-a!r*d^e.r",
+                "http.header.Repeat-ed",
+                "http.header.Repeat*ed",
+                "http.header.Same-URL"
+            );
+            js = Models.bind(new JSONik(), newContext());
+            js.applyBindings();
+
+            js.fetchPeopleWithHeaders(url, "easy", "harder", "rep");
+        }
+
+        if (0 == js.getFetchedCount()) {
+            throw new InterruptedException();
+        }
+
+        assertEquals(js.getFetchedCount(), 1, "One person loaded: " + js.getFetchedCount());
+
+        Person p = js.getFetched();
+
+        assertNotNull(p, "We should get our person back: " + p);
+        assertEquals("easyharderreprep" + url, p.getFirstName(), "Expecting header mess: " + p.getFirstName());
+        assertEquals(Sex.FEMALE, p.getSex(), "Expecting FEMALE: " + p.getSex());
+    }
+
+    @OnReceive(url="{url}", headers={
+        "Easy: {easy}",
+        "H-a!r*d^e.r: {harder}",
+        "Repeat-ed: {rep}",
+        "Repeat*ed: {rep}",
+        "Same-URL: {url}"
+    })
+    static void fetchPeopleWithHeaders(JSONik model, People p) {
+        final int size = p.getInfo().size();
+        if (size > 0) {
+            model.setFetched(p.getInfo().get(0));
+        }
+        model.setFetchedCount(size);
+    }
+
+    @KOTest public void loadAndParseArrayOfIntegers() throws InterruptedException {
+        if (js == null) {
+            url = Utils.prepareURL(
+                JSONTest.class, "{'age':[1, 2, 3]}",
+                "application/json"
+            );
+            js = Models.bind(new JSONik(), newContext());
+            js.applyBindings();
+
+            js.fetchPeopleAge(url);
+        }
+
+        if (0 == js.getFetchedCount()) {
+            throw new InterruptedException();
+        }
+
+        assertEquals(js.getFetchedCount(), 6, "1 + 2 + 3 is " + js.getFetchedCount());
+    }
+
+    @OnReceive(url="{url}")
+    static void fetchPeopleSex(JSONik model, People p) {
+        model.setFetchedCount(1);
+        model.getFetchedSex().addAll(p.getSex());
+    }
+
+    @KOTest public void loadAndParseArrayOfEnums() throws InterruptedException {
+        if (js == null) {
+            url = Utils.prepareURL(
+                JSONTest.class, "{'sex':['FEMALE', 'MALE', 'MALE']}",
+                "application/json"
+            );
+            js = Models.bind(new JSONik(), newContext());
+            js.applyBindings();
+
+            js.fetchPeopleSex(url);
+        }
+
+        if (0 == js.getFetchedCount()) {
+            throw new InterruptedException();
+        }
+
+        assertEquals(js.getFetchedCount(), 1, "Loaded");
+
+        assertEquals(js.getFetchedSex().size(), 3, "Three values " + js.getFetchedSex());
+        assertEquals(js.getFetchedSex().get(0), Sex.FEMALE, "Female first " + js.getFetchedSex());
+        assertEquals(js.getFetchedSex().get(1), Sex.MALE, "male 2nd " + js.getFetchedSex());
+        assertEquals(js.getFetchedSex().get(2), Sex.MALE, "male 3rd " + js.getFetchedSex());
+    }
+
+    @KOTest public void loadAndParseJSONArray() throws InterruptedException {
+        if (js == null) {
+            url = Utils.prepareURL(
+                JSONTest.class, "[{'firstName': 'Gitar', 'sex': 'FEMALE'},"
+                + "{'firstName': 'Peter', 'sex': 'MALE'}"
+                + "]",
+                "application/json"
+            );
+            js = Models.bind(new JSONik(), newContext());
+            js.applyBindings();
+            js.setFetched(null);
+
+            js.fetchArray(url);
+        }
+
+
+        Person p = js.getFetched();
+        if (p == null) {
+            throw new InterruptedException();
+        }
+
+        assertEquals(js.getFetchedCount(), 2, "We got two values: " + js.getFetchedCount());
+        assertEquals("Gitar", p.getFirstName(), "Expecting Gitar: " + p.getFirstName());
+        assertEquals(Sex.FEMALE, p.getSex(), "Expecting FEMALE: " + p.getSex());
+    }
+
+    @KOTest public void loadError() throws InterruptedException {
+        if (js == null) {
+            js = Models.bind(new JSONik(), newContext());
+            js.applyBindings();
+            js.setFetched(null);
+
+            js.fetchArray("http://127.0.0.1:54253/does/not/exist.txt");
+        }
+
+
+        if (js.getFetchedResponse() == null) {
+            throw new InterruptedException();
+        }
+
+        assertEquals("Exception", js.getFetchedResponse(), "Response " + js.getFetchedResponse());
+    }
+
+    @Model(className = "NameAndValue", properties = {
+        @Property(name = "name", type = String.class),
+        @Property(name = "value", type = long.class),
+        @Property(name = "small", type = byte.class)
+    })
+    static class NandV {
+    }
+
+    @KOTest public void parseNullNumber() throws Exception {
+        String txt = "{ \"name\":\"M\" }";
+        ByteArrayInputStream is = new ByteArrayInputStream(txt.getBytes("UTF-8"));
+        NameAndValue v = Models.parse(newContext(), NameAndValue.class, is);
+        assertEquals("M", v.getName(), "Name is 'M': " + v.getName());
+        assertEquals(0L, v.getValue(), "Value is empty: " + v.getValue());
+        assertEquals((byte)0, v.getSmall(), "Small value is empty: " + v.getSmall());
+    }
+
+    @KOTest public void deserializeWrongEnum() throws Exception {
+        PrintStream prev;
+        ByteArrayOutputStream err = new ByteArrayOutputStream();
+        try {
+            prev = System.err;
+            System.setErr(new PrintStream(err));
+        } catch (SecurityException e) {
+            err = null;
+            prev = null;
+        } catch (LinkageError e) {
+            err = null;
+            prev = null;
+        }
+
+        String str = "{ \"sex\" : \"unknown\" }";
+        ByteArrayInputStream is = new ByteArrayInputStream(str.getBytes("UTF-8"));
+        Person p = Models.parse(newContext(), Person.class, is);
+        assertNull(p.getSex(), "Wrong sex means null, but was: " + p.getSex());
+
+        if (err != null) {
+            assertTrue(err.toString().contains("unknown") && err.toString().contains("Sex"), "Expecting error: " + err.toString());
+        }
+        if (prev != null) {
+            try {
+                System.setErr(prev);
+            } catch (LinkageError e) {
+                // ignore
+            }
+        }
+    }
+
+
+    private static BrwsrCtx newContext() {
+        return Utils.newContext(JSONTest.class);
+    }
+
+}


[13/24] incubator-netbeans-html4j git commit: [INFRA-15006] Initial donation of HTML/Java NetBeans API. Equivalent to the content of html4j-donation-review.zip donated as part of ApacheNetBeansDonation1.zip with SHA256 being 7f2ca0f61953a190613c9a0fbcc1b

Posted by jt...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json-tck/src/main/java/net/java/html/json/tests/KnockoutTest.java
----------------------------------------------------------------------
diff --git a/json-tck/src/main/java/net/java/html/json/tests/KnockoutTest.java b/json-tck/src/main/java/net/java/html/json/tests/KnockoutTest.java
new file mode 100644
index 0000000..b5e34a6
--- /dev/null
+++ b/json-tck/src/main/java/net/java/html/json/tests/KnockoutTest.java
@@ -0,0 +1,1001 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json.tests;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+import net.java.html.BrwsrCtx;
+import net.java.html.json.ComputedProperty;
+import net.java.html.json.Function;
+import net.java.html.json.Model;
+import net.java.html.json.Models;
+import net.java.html.json.Property;
+import org.netbeans.html.json.tck.KOTest;
+import static net.java.html.json.tests.Utils.assertEquals;
+import static net.java.html.json.tests.Utils.assertNotNull;
+import static net.java.html.json.tests.Utils.assertTrue;
+import static net.java.html.json.tests.Utils.assertFalse;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+@Model(className="KnockoutModel", targetId = "", properties={
+    @Property(name="name", type=String.class),
+    @Property(name="results", type=String.class, array = true),
+    @Property(name="numbers", type=int.class, array = true),
+    @Property(name="callbackCount", type=int.class),
+    @Property(name="people", type=PersonImpl.class, array = true),
+    @Property(name="enabled", type=boolean.class),
+    @Property(name="latitude", type=double.class),
+    @Property(name="choice", type=KnockoutTest.Choice.class),
+    @Property(name="archetype", type=ArchetypeData.class),
+    @Property(name="archetypes", type=ArchetypeData.class, array = true),
+})
+public final class KnockoutTest {
+    private KnockoutModel js;
+
+    enum Choice {
+        A, B;
+    }
+
+    @ComputedProperty static List<Integer> resultLengths(List<String> results) {
+        Integer[] arr = new Integer[results.size()];
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = results.get(i).length();
+        }
+        return Arrays.asList(arr);
+    }
+
+    @KOTest public void modifyValueAssertChangeInModelOnEnum() throws Throwable {
+        Object exp = Utils.exposeHTML(KnockoutTest.class,
+            "Latitude: <input id='input' data-bind=\"value: choice\"></input>\n"
+        );
+        try {
+
+            KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
+            m.setChoice(Choice.A);
+            m.applyBindings();
+
+            String v = getSetInput("input", null);
+            assertEquals("A", v, "Value is really A: " + v);
+
+            getSetInput("input", "B");
+            triggerEvent("input", "change");
+
+            assertEquals(Choice.B, m.getChoice(), "Enum property updated: " + m.getChoice());
+        } catch (Throwable t) {
+            throw t;
+        } finally {
+            Utils.exposeHTML(KnockoutTest.class, "");
+        }
+    }
+
+
+    @KOTest public void modifyRadioValueOnEnum() throws Throwable {
+        Object exp = Utils.exposeHTML(KnockoutTest.class,
+            "<input id='i1' type=\"radio\" name=\"choice\" value=\"A\" data-bind=\"checked: choice\"></input>Right\n" +
+            "<input id='input' type=\"radio\" name=\"choice\" value=\"B\" data-bind=\"checked: choice\"></input>Never\n" +
+            "\n"
+        );
+        try {
+
+            KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
+            m.setChoice(Choice.B);
+            m.applyBindings();
+
+            assertFalse(isChecked("i1"), "B should be checked now");
+            assertTrue(isChecked("input"), "B should be checked now");
+
+            triggerEvent("i1", "click");
+            assertEquals(Choice.A, m.getChoice(), "Switched to A");
+            assertTrue(isChecked("i1"), "A should be checked now");
+            assertFalse(isChecked("input"), "A should be checked now");
+
+            triggerEvent("input", "click");
+
+            assertEquals(Choice.B, m.getChoice(), "Enum property updated: " + m.getChoice());
+        } catch (Throwable t) {
+            throw t;
+        } finally {
+            Utils.exposeHTML(KnockoutTest.class, "");
+        }
+    }
+
+    @KOTest public void modifyValueAssertChangeInModelOnDouble() throws Throwable {
+        Object exp = Utils.exposeHTML(KnockoutTest.class,
+            "Latitude: <input id='input' data-bind=\"value: latitude\"></input>\n"
+        );
+        try {
+
+            KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
+            m.setLatitude(50.5);
+            m.applyBindings();
+
+            String v = getSetInput("input", null);
+            assertEquals("50.5", v, "Value is really 50.5: " + v);
+
+            getSetInput("input", "49.5");
+            triggerEvent("input", "change");
+
+            assertEquals(49.5, m.getLatitude(), "Double property updated: " + m.getLatitude());
+        } catch (Throwable t) {
+            throw t;
+        } finally {
+            Utils.exposeHTML(KnockoutTest.class, "");
+        }
+    }
+
+    @KOTest public void rawObject() throws Exception {
+        if (js == null) {
+            final BrwsrCtx ctx = newContext();
+            Person p1 = Models.bind(new Person(), ctx);
+            p1.setFirstName("Jarda");
+            p1.setLastName("Tulach");
+            Object raw = Models.toRaw(p1);
+            Person p2 = Models.fromRaw(ctx, Person.class, raw);
+
+            assertEquals(p2.getFirstName(), "Jarda", "First name");
+            assertEquals(p2.getLastName(), "Tulach", "Last name");
+
+            p2.setFirstName("Jirka");
+            assertEquals(p2.getFirstName(), "Jirka", "First name updated");
+
+            js = new KnockoutModel();
+            js.getPeople().add(p1);
+            js.getPeople().add(p2);
+        }
+
+        Person p1 = js.getPeople().get(0);
+        Person p2 = js.getPeople().get(1);
+
+        if (js.getPeople().size() == 2) {
+            if (!"Jirka".equals(p1.getFirstName())) {
+                throw new InterruptedException();
+            }
+
+            assertEquals(p1.getFirstName(), "Jirka", "First name updated in original object");
+
+            p1.setFirstName("Ondra");
+            assertEquals(p1.getFirstName(), "Ondra", "1st name updated in original object");
+
+            js.getPeople().add(p1);
+        }
+
+        if (!"Ondra".equals(p2.getFirstName())) {
+            throw new InterruptedException();
+        }
+        assertEquals(p2.getFirstName(), "Ondra", "1st name updated in copied object");
+    }
+
+    @KOTest public void modifyComputedProperty() throws Throwable {
+        Object exp = Utils.exposeHTML(KnockoutTest.class,
+            "Full name: <div data-bind='with:firstPerson'>\n"
+                + "<input id='input' data-bind=\"value: fullName\"></input>\n"
+                + "</div>\n"
+        );
+        try {
+            KnockoutModel m = new KnockoutModel();
+            m.getPeople().add(new Person());
+
+            m = Models.bind(m, newContext());
+            m.getFirstPerson().setFirstName("Jarda");
+            m.getFirstPerson().setLastName("Tulach");
+            m.applyBindings();
+
+            String v = getSetInput("input", null);
+            assertEquals("Jarda Tulach", v, "Value: " + v);
+
+            getSetInput("input", "Mickey Mouse");
+            triggerEvent("input", "change");
+
+            assertEquals("Mickey", m.getFirstPerson().getFirstName(), "First name updated");
+            assertEquals("Mouse", m.getFirstPerson().getLastName(), "Last name updated");
+        } catch (Throwable t) {
+            throw t;
+        } finally {
+            Utils.exposeHTML(KnockoutTest.class, "");
+        }
+    }
+
+    @KOTest public void modifyValueAssertChangeInModelOnBoolean() throws Throwable {
+        Object exp = Utils.exposeHTML(KnockoutTest.class,
+            "Latitude: <input id='input' data-bind=\"value: enabled\"></input>\n"
+        );
+        try {
+
+            KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
+            m.setEnabled(true);
+            m.applyBindings();
+
+            String v = getSetInput("input", null);
+            assertEquals("true", v, "Value is really true: " + v);
+
+            getSetInput("input", "false");
+            triggerEvent("input", "change");
+
+            assertFalse(m.isEnabled(), "Boolean property updated: " + m.isEnabled());
+        } catch (Throwable t) {
+            throw t;
+        } finally {
+            Utils.exposeHTML(KnockoutTest.class, "");
+        }
+    }
+
+    @KOTest public void modifyValueAssertChangeInModel() throws Exception {
+        Object exp = Utils.exposeHTML(KnockoutTest.class,
+            "<h1 data-bind=\"text: helloMessage\">Loading Bck2Brwsr's Hello World...</h1>\n" +
+            "Your name: <input id='input' data-bind=\"value: name\"></input>\n" +
+            "<button id=\"hello\">Say Hello!</button>\n"
+        );
+        try {
+
+            KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
+            m.setName("Kukuc");
+            m.applyBindings();
+
+            String v = getSetInput("input", null);
+            assertEquals("Kukuc", v, "Value is really kukuc: " + v);
+
+            getSetInput("input", "Jardo");
+            triggerEvent("input", "change");
+
+            assertEquals("Jardo", m.getName(), "Name property updated: " + m.getName());
+        } finally {
+            Utils.exposeHTML(KnockoutTest.class, "");
+        }
+    }
+
+    private static String getSetSelected(int index, Object value) throws Exception {
+        String s = "var index = arguments[0];\n"
+        + "var value = arguments[1];\n"
+        + "var n = window.document.getElementById('input'); \n "
+        + "if (value != null) {\n"
+        + "  n.options[index].value = 'me'; \n"
+        + "  n.value = 'me'; \n"
+        + "  ko.dataFor(n.options[index]).archetype(value); // haven't found better way to trigger ko change yet \n"
+        + "} \n "
+        + "var op = n.options[n.selectedIndex]; \n"
+        + "return op ? op.text : n.selectedIndex;\n";
+        Object ret = Utils.executeScript(
+            KnockoutTest.class,
+            s, index, value
+        );
+        return ret == null ? null : ret.toString();
+    }
+
+    @Model(className = "ArchetypeData", properties = {
+        @Property(name = "artifactId", type = String.class),
+        @Property(name = "groupId", type = String.class),
+        @Property(name = "version", type = String.class),
+        @Property(name = "name", type = String.class),
+        @Property(name = "description", type = String.class),
+        @Property(name = "url", type = String.class),
+    })
+    static class ArchModel {
+    }
+
+    @KOTest public void selectWorksOnModels() throws Exception {
+        if (js == null) {
+            Utils.exposeHTML(KnockoutTest.class,
+                "<select id='input' data-bind=\"options: archetypes,\n" +
+"                       optionsText: 'name',\n" +
+"                       value: archetype\">\n" +
+"                  </select>\n" +
+""
+            );
+
+            js = Models.bind(new KnockoutModel(), newContext());
+            js.getArchetypes().add(new ArchetypeData("ko4j", "org.netbeans.html", "0.8.3", "ko4j", "ko4j", null));
+            js.getArchetypes().add(new ArchetypeData("crud", "org.netbeans.html", "0.8.3", "crud", "crud", null));
+            js.getArchetypes().add(new ArchetypeData("3rd", "org.netbeans.html", "0.8.3", "3rd", "3rd", null));
+            js.setArchetype(js.getArchetypes().get(1));
+            js.applyBindings();
+
+            String v = getSetSelected(0, null);
+            assertEquals("crud", v, "Second index (e.g. crud) is selected: " + v);
+
+            String sel = getSetSelected(2, Models.toRaw(js.getArchetypes().get(2)));
+            assertEquals("3rd", sel, "3rd is selected now: " + sel);
+        }
+
+        if (js.getArchetype() != js.getArchetypes().get(2)) {
+            throw new InterruptedException();
+        }
+
+        Utils.exposeHTML(KnockoutTest.class, "");
+    }
+
+    @KOTest public void nestedObjectEqualsChange() throws Exception {
+        nestedObjectEqualsChange(true);
+    }
+
+    @KOTest public void nestedObjectChange() throws Exception {
+        nestedObjectEqualsChange(false);
+    }
+    private  void nestedObjectEqualsChange(boolean preApply) throws Exception {
+        Utils.exposeHTML(KnockoutTest.class,
+"            <div data-bind='with: archetype'>\n" +
+"                <input id='input' data-bind='value: groupId'></input>\n" +
+"            </div>\n"
+        );
+
+        js = Models.bind(new KnockoutModel(), newContext());
+        if (preApply) {
+            js.applyBindings();
+        }
+        js.setArchetype(new ArchetypeData());
+        js.getArchetype().setGroupId("org.netbeans.html");
+        js.applyBindings();
+
+        String v = getSetInput("input", null);
+        assertEquals("org.netbeans.html", v, "groupId has been changed");
+        Utils.exposeHTML(KnockoutTest.class, "");
+    }
+
+    @KOTest public void modifyValueAssertAsyncChangeInModel() throws Exception {
+        if (js == null) {
+            Utils.exposeHTML(KnockoutTest.class,
+                "<h1 data-bind=\"text: helloMessage\">Loading Bck2Brwsr's Hello World...</h1>\n" +
+                "Your name: <input id='input' data-bind=\"value: name\"></input>\n" +
+                "<button id=\"hello\">Say Hello!</button>\n"
+            );
+
+            js = Models.bind(new KnockoutModel(), newContext());
+            js.setName("Kukuc");
+            js.applyBindings();
+
+            String v = getSetInput("input", null);
+            assertEquals("Kukuc", v, "Value is really kukuc: " + v);
+
+            Timer t = new Timer("Set to Jardo");
+            t.schedule(new TimerTask() {
+                @Override
+                public void run() {
+                    js.setName("Jardo");
+                }
+            }, 1);
+        }
+
+        String v = getSetInput("input", null);
+        if (!"Jardo".equals(v)) {
+            throw new InterruptedException();
+        }
+
+        Utils.exposeHTML(KnockoutTest.class, "");
+    }
+
+    @Model(className = "ConstantModel", targetId = "", builder = "assign", properties = {
+        @Property(name = "doubleValue", mutable = false, type = double.class),
+        @Property(name = "stringValue", mutable = false, type = String.class),
+        @Property(name = "boolValue", mutable = false, type = boolean.class),
+        @Property(name = "intArray", mutable = false, type = int.class, array = true),
+    })
+    static class ConstantCntrl {
+    }
+
+    @KOTest public void nonMutableDouble() throws Exception {
+        Utils.exposeHTML(KnockoutTest.class,
+            "Type: <input id='input' data-bind=\"value: typeof doubleValue\"></input>\n"
+        );
+
+        ConstantModel model = Models.bind(new ConstantModel(), newContext());
+        model.assignStringValue("Hello").assignDoubleValue(10.0);
+        model.applyBindings();
+
+        String v = getSetInput("input", null);
+        assertEquals(v, "number", "Right type found: " + v);
+
+        Utils.exposeHTML(KnockoutTest.class, "");
+    }
+
+    @KOTest public void nonMutableString() throws Exception {
+        Utils.exposeHTML(KnockoutTest.class,
+            "Type: <input id='input' data-bind=\"value: typeof stringValue\"></input>\n"
+        );
+
+        ConstantModel model = Models.bind(new ConstantModel(), newContext());
+        model.assignStringValue("Hello").assignDoubleValue(10.0);
+        model.applyBindings();
+
+        String v = getSetInput("input", null);
+        assertEquals(v, "string", "Right type found: " + v);
+
+        Utils.exposeHTML(KnockoutTest.class, "");
+    }
+
+    @KOTest public void nonMutableBoolean() throws Exception {
+        Utils.exposeHTML(KnockoutTest.class,
+            "Type: <input id='input' data-bind=\"value: typeof boolValue\"></input>\n"
+        );
+
+        ConstantModel model = Models.bind(new ConstantModel(), newContext());
+        model.assignStringValue("Hello").assignBoolValue(true);
+        model.applyBindings();
+
+        String v = getSetInput("input", null);
+        assertEquals(v, "boolean", "Right type found: " + v);
+
+        Utils.exposeHTML(KnockoutTest.class, "");
+    }
+
+    @KOTest public void nonMutableIntArray() throws Exception {
+        Utils.exposeHTML(KnockoutTest.class,
+            "Type: <input id='input' data-bind=\"value: typeof intArray\"></input>\n"
+        );
+
+        ConstantModel model = Models.bind(new ConstantModel(), newContext());
+        model.assignStringValue("Hello").assignDoubleValue(Long.MAX_VALUE).assignIntArray(1, 2, 3, 4);
+        model.applyBindings();
+
+        String v = getSetInput("input", null);
+        assertEquals(v, "object", "Right type found: " + v);
+
+        Utils.exposeHTML(KnockoutTest.class, "");
+    }
+
+    private static String getSetInput(String id, String value) throws Exception {
+        String s = "var value = arguments[0];\n"
+        + "var n = window.document.getElementById(arguments[1]); \n "
+        + "if (value != null) n['value'] = value; \n "
+        + "return n['value'];";
+        Object ret = Utils.executeScript(
+            KnockoutTest.class,
+            s, value, id
+        );
+        return ret == null ? null : ret.toString();
+    }
+
+    private static boolean isChecked(String id) throws Exception {
+        String s = ""
+        + "var n = window.document.getElementById(arguments[0]); \n "
+        + "return n['checked'];";
+        Object ret = Utils.executeScript(
+            KnockoutTest.class,
+            s, id
+        );
+        return Boolean.TRUE.equals(ret);
+    }
+
+    public static void triggerEvent(String id, String ev) throws Exception {
+        Utils.executeScript(
+            KnockoutTest.class,
+            "ko.utils.triggerEvent(window.document.getElementById(arguments[0]), arguments[1]);",
+            id, ev
+        );
+    }
+
+    @KOTest public void displayContentOfArray() throws Exception {
+        Object exp = Utils.exposeHTML(KnockoutTest.class,
+            "<ul id='ul' data-bind='foreach: results'>\n"
+            + "  <li data-bind='text: $data, click: $root.call'/>\n"
+            + "</ul>\n"
+        );
+        try {
+            KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
+            m.getResults().add("Ahoj");
+            m.applyBindings();
+
+            int cnt = Utils.countChildren(KnockoutTest.class, "ul");
+            assertEquals(cnt, 1, "One child, but was " + cnt);
+
+            m.getResults().add("Hi");
+
+            cnt = Utils.countChildren(KnockoutTest.class, "ul");
+            assertEquals(cnt, 2, "Two children now, but was " + cnt);
+
+            triggerChildClick("ul", 1);
+
+            assertEquals(1, m.getCallbackCount(), "One callback " + m.getCallbackCount());
+            assertEquals("Hi", m.getName(), "We got callback from 2nd child " + m.getName());
+        } finally {
+            Utils.exposeHTML(KnockoutTest.class, "");
+        }
+    }
+
+    @KOTest public void displayContentOfAsyncArray() throws Exception {
+        if (js == null) {
+            Utils.exposeHTML(KnockoutTest.class,
+                "<ul id='ul' data-bind='foreach: results'>\n"
+                + "  <li data-bind='text: $data, click: $root.call'/>\n"
+                + "</ul>\n"
+            );
+            js = Models.bind(new KnockoutModel(), newContext());
+            js.getResults().add("Ahoj");
+            js.applyBindings();
+
+            int cnt = Utils.countChildren(KnockoutTest.class, "ul");
+            assertEquals(cnt, 1, "One child, but was " + cnt);
+
+            Timer t = new Timer("add to array");
+            t.schedule(new TimerTask() {
+                @Override
+                public void run() {
+                    js.getResults().add("Hi");
+                }
+            }, 1);
+        }
+
+
+        int cnt = Utils.countChildren(KnockoutTest.class, "ul");
+        if (cnt != 2) {
+            throw new InterruptedException();
+        }
+
+        try {
+            triggerChildClick("ul", 1);
+
+            assertEquals(1, js.getCallbackCount(), "One callback " + js.getCallbackCount());
+            assertEquals("Hi", js.getName(), "We got callback from 2nd child " + js.getName());
+        } finally {
+            Utils.exposeHTML(KnockoutTest.class, "");
+        }
+    }
+
+    @KOTest public void displayContentOfComputedArray() throws Exception {
+        Object exp = Utils.exposeHTML(KnockoutTest.class,
+            "<ul id='ul' data-bind='foreach: bothNames'>\n"
+            + "  <li data-bind='text: $data, click: $root.assignFirstName'/>\n"
+            + "</ul>\n"
+        );
+        try {
+            Pair m = Models.bind(new Pair("First", "Last", null), newContext());
+            m.applyBindings();
+
+            int cnt = Utils.countChildren(KnockoutTest.class, "ul");
+            assertEquals(cnt, 2, "Two children now, but was " + cnt);
+
+            triggerChildClick("ul", 1);
+
+            assertEquals("Last", m.getFirstName(), "We got callback from 2nd child " + m.getFirstName());
+
+            m.setLastName("Verylast");
+
+            cnt = Utils.countChildren(KnockoutTest.class, "ul");
+            assertEquals(cnt, 2, "Two children now, but was " + cnt);
+
+            triggerChildClick("ul", 1);
+
+            assertEquals("Verylast", m.getFirstName(), "We got callback from 2nd child " + m.getFirstName());
+
+        } finally {
+            Utils.exposeHTML(KnockoutTest.class, "");
+        }
+    }
+
+    @KOTest public void displayContentOfComputedArrayOnASubpair() throws Exception {
+        Object exp = Utils.exposeHTML(KnockoutTest.class,
+              "<div data-bind='with: next'>\n"
+            + "<ul id='ul' data-bind='foreach: bothNames'>\n"
+            + "  <li data-bind='text: $data, click: $root.assignFirstName'/>\n"
+            + "</ul>"
+            + "</div>\n"
+        );
+        try {
+            final BrwsrCtx ctx = newContext();
+            Pair m = Models.bind(new Pair(null, null, new Pair("First", "Last", null)), ctx);
+            m.applyBindings();
+
+            int cnt = Utils.countChildren(KnockoutTest.class, "ul");
+            assertEquals(cnt, 2, "Two children now, but was " + cnt);
+
+            triggerChildClick("ul", 1);
+
+            assertEquals(PairModel.ctx, ctx, "Context remains the same");
+
+            assertEquals("Last", m.getFirstName(), "We got callback from 2nd child " + m.getFirstName());
+        } finally {
+            Utils.exposeHTML(KnockoutTest.class, "");
+        }
+    }
+
+    @KOTest public void displayContentOfComputedArrayOnComputedASubpair() throws Exception {
+        Object exp = Utils.exposeHTML(KnockoutTest.class,
+              "<div data-bind='with: nextOne'>\n"
+            + "<ul id='ul' data-bind='foreach: bothNames'>\n"
+            + "  <li data-bind='text: $data, click: $root.assignFirstName'/>\n"
+            + "</ul>"
+            + "</div>\n"
+        );
+        try {
+            Pair m = Models.bind(new Pair(null, null, new Pair("First", "Last", null)), newContext());
+            m.applyBindings();
+
+            int cnt = Utils.countChildren(KnockoutTest.class, "ul");
+            assertEquals(cnt, 2, "Two children now, but was " + cnt);
+
+            triggerChildClick("ul", 1);
+
+            assertEquals("Last", m.getFirstName(), "We got callback from 2nd child " + m.getFirstName());
+        } finally {
+            Utils.exposeHTML(KnockoutTest.class, "");
+        }
+    }
+
+    @KOTest public void checkBoxToBooleanBinding() throws Exception {
+        Object exp = Utils.exposeHTML(KnockoutTest.class,
+            "<input type='checkbox' id='b' data-bind='checked: enabled'></input>\n"
+        );
+        try {
+            KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
+            m.applyBindings();
+
+            assertFalse(m.isEnabled(), "Is disabled");
+
+            triggerClick("b");
+
+            assertTrue(m.isEnabled(), "Now the model is enabled");
+        } finally {
+            Utils.exposeHTML(KnockoutTest.class, "");
+        }
+    }
+
+
+
+    @KOTest public void displayContentOfDerivedArray() throws Exception {
+        Object exp = Utils.exposeHTML(KnockoutTest.class,
+            "<ul id='ul' data-bind='foreach: cmpResults'>\n"
+            + "  <li><b data-bind='text: $data'></b></li>\n"
+            + "</ul>\n"
+        );
+        try {
+            KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
+            m.getResults().add("Ahoj");
+            m.applyBindings();
+
+            int cnt = Utils.countChildren(KnockoutTest.class, "ul");
+            assertEquals(cnt, 1, "One child, but was " + cnt);
+
+            m.getResults().add("hello");
+
+            cnt = Utils.countChildren(KnockoutTest.class, "ul");
+            assertEquals(cnt, 2, "Two children now, but was " + cnt);
+        } finally {
+            Utils.exposeHTML(KnockoutTest.class, "");
+        }
+    }
+
+    @KOTest public void displayContentOfArrayOfPeople() throws Exception {
+        Object exp = Utils.exposeHTML(KnockoutTest.class,
+            "<ul id='ul' data-bind='foreach: people'>\n"
+            + "  <li data-bind='text: $data.firstName, click: $root.removePerson'></li>\n"
+            + "</ul>\n"
+        );
+        try {
+            final BrwsrCtx c = newContext();
+            KnockoutModel m = Models.bind(new KnockoutModel(), c);
+
+            final Person first = Models.bind(new Person(), c);
+            first.setFirstName("first");
+            m.getPeople().add(first);
+
+            m.applyBindings();
+
+            int cnt = Utils.countChildren(KnockoutTest.class, "ul");
+            assertEquals(cnt, 1, "One child, but was " + cnt);
+
+            final Person second = Models.bind(new Person(), c);
+            second.setFirstName("second");
+            m.getPeople().add(second);
+
+            cnt = Utils.countChildren(KnockoutTest.class, "ul");
+            assertEquals(cnt, 2, "Two children now, but was " + cnt);
+
+            triggerChildClick("ul", 1);
+
+            assertEquals(1, m.getCallbackCount(), "One callback " + m.getCallbackCount());
+
+            cnt = Utils.countChildren(KnockoutTest.class, "ul");
+            assertEquals(cnt , 1, "Again one child, but was " + cnt);
+
+            String txt = childText("ul", 0);
+            assertEquals("first", txt, "Expecting 'first': " + txt);
+
+            first.setFirstName("changed");
+
+            txt = childText("ul", 0);
+            assertEquals("changed", txt, "Expecting 'changed': " + txt);
+        } finally {
+            Utils.exposeHTML(KnockoutTest.class, "");
+        }
+    }
+
+    @ComputedProperty
+    static Person firstPerson(List<Person> people) {
+        return people.isEmpty() ? null : people.get(0);
+    }
+
+    @KOTest public void accessFirstPersonWithOnFunction() throws Exception {
+        Object exp = Utils.exposeHTML(KnockoutTest.class,
+            "<p id='ul' data-bind='with: firstPerson'>\n"
+            + "  <span data-bind='text: firstName, click: changeSex'></span>\n"
+            + "</p>\n"
+        );
+        try {
+            trasfertToFemale();
+        } finally {
+            Utils.exposeHTML(KnockoutTest.class, "");
+        }
+    }
+
+    @KOTest public void onPersonFunction() throws Exception {
+        Object exp = Utils.exposeHTML(KnockoutTest.class,
+            "<ul id='ul' data-bind='foreach: people'>\n"
+            + "  <li data-bind='text: $data.firstName, click: changeSex'></li>\n"
+            + "</ul>\n"
+        );
+        try {
+            trasfertToFemale();
+        } finally {
+            Utils.exposeHTML(KnockoutTest.class, "");
+        }
+    }
+
+    private void trasfertToFemale() throws Exception {
+        KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
+
+        final Person first = Models.bind(new Person(), newContext());
+        first.setFirstName("first");
+        first.setSex(Sex.MALE);
+        m.getPeople().add(first);
+
+
+        m.applyBindings();
+
+        int cnt = Utils.countChildren(KnockoutTest.class, "ul");
+        assertEquals(cnt, 1, "One child, but was " + cnt);
+
+
+        triggerChildClick("ul", 0);
+
+        assertEquals(first.getSex(), Sex.FEMALE, "Transverted to female: " + first.getSex());
+    }
+
+    @KOTest public void stringArrayModificationVisible() throws Exception {
+        Object exp = Utils.exposeHTML(KnockoutTest.class,
+                "<div>\n"
+                + "<ul id='ul' data-bind='foreach: results'>\n"
+                + "  <li data-bind='text: $data'></li>\n"
+                + "</ul>\n"
+              + "</div>\n"
+        );
+        try {
+            KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
+            m.getResults().add("Ahoj");
+            m.getResults().add("Hello");
+            m.applyBindings();
+
+            int cnt = Utils.countChildren(KnockoutTest.class, "ul");
+            assertEquals(cnt, 2, "Two children " + cnt);
+
+            Object arr = Utils.addChildren(KnockoutTest.class, "ul", "results", "Hi");
+            assertTrue(arr instanceof Object[], "Got back an array: " + arr);
+            final int len = ((Object[])arr).length;
+
+            assertEquals(len, 3, "Three elements in the array " + len);
+
+            int newCnt = Utils.countChildren(KnockoutTest.class, "ul");
+            assertEquals(newCnt, 3, "Three children in the DOM: " + newCnt);
+
+            assertEquals(m.getResults().size(), 3, "Three java strings: " + m.getResults());
+        } finally {
+            Utils.exposeHTML(KnockoutTest.class, "");
+        }
+    }
+
+    @KOTest public void intArrayModificationVisible() throws Exception {
+        Object exp = Utils.exposeHTML(KnockoutTest.class,
+                "<div>\n"
+                + "<ul id='ul' data-bind='foreach: numbers'>\n"
+                + "  <li data-bind='text: $data'></li>\n"
+                + "</ul>\n"
+              + "</div>\n"
+        );
+        try {
+            KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
+            m.getNumbers().add(1);
+            m.getNumbers().add(31);
+            m.applyBindings();
+
+            int cnt = Utils.countChildren(KnockoutTest.class, "ul");
+            assertEquals(cnt, 2, "Two children " + cnt);
+
+            Object arr = Utils.addChildren(KnockoutTest.class, "ul", "numbers", 42);
+            assertTrue(arr instanceof Object[], "Got back an array: " + arr);
+            final int len = ((Object[])arr).length;
+
+            assertEquals(len, 3, "Three elements in the array " + len);
+
+            int newCnt = Utils.countChildren(KnockoutTest.class, "ul");
+            assertEquals(newCnt, 3, "Three children in the DOM: " + newCnt);
+
+            assertEquals(m.getNumbers().size(), 3, "Three java ints: " + m.getNumbers());
+            assertEquals(m.getNumbers().get(2), 42, "Meaning of world: " + m.getNumbers());
+        } finally {
+            Utils.exposeHTML(KnockoutTest.class, "");
+        }
+    }
+
+    @KOTest public void derivedIntArrayModificationVisible() throws Exception {
+        Object exp = Utils.exposeHTML(KnockoutTest.class,
+                "<div>\n"
+                + "<ul id='ul' data-bind='foreach: resultLengths'>\n"
+                + "  <li data-bind='text: $data'></li>\n"
+                + "</ul>\n"
+              + "</div>\n"
+        );
+        try {
+            KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
+            m.getResults().add("Ahoj");
+            m.getResults().add("Hello");
+            m.applyBindings();
+
+            int cnt = Utils.countChildren(KnockoutTest.class, "ul");
+            assertEquals(cnt, 2, "Two children " + cnt);
+
+            Object arr = Utils.addChildren(KnockoutTest.class, "ul", "results", "Hi");
+            assertTrue(arr instanceof Object[], "Got back an array: " + arr);
+            final int len = ((Object[])arr).length;
+
+            assertEquals(len, 3, "Three elements in the array " + len);
+
+            int newCnt = Utils.countChildren(KnockoutTest.class, "ul");
+            assertEquals(newCnt, 3, "Three children in the DOM: " + newCnt);
+
+            assertEquals(m.getResultLengths().size(), 3, "Three java ints: " + m.getResultLengths());
+            assertEquals(m.getResultLengths().get(2), 2, "Size is two: " + m.getResultLengths());
+        } finally {
+            Utils.exposeHTML(KnockoutTest.class, "");
+        }
+    }
+
+    @KOTest public void archetypeArrayModificationVisible() throws Exception {
+        Object exp = Utils.exposeHTML(KnockoutTest.class,
+                "<div>\n"
+                + "<ul id='ul' data-bind='foreach: archetypes'>\n"
+                + "  <li data-bind='text: artifactId'></li>\n"
+                + "</ul>\n"
+              + "</div>\n"
+        );
+        try {
+            KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
+            m.applyBindings();
+
+            int cnt = Utils.countChildren(KnockoutTest.class, "ul");
+            assertEquals(cnt, 0, "No children " + cnt);
+
+            Object arr = Utils.addChildren(KnockoutTest.class, "ul", "archetypes", new ArchetypeData("aid", "gid", "v", "n", "d", "u"));
+            assertTrue(arr instanceof Object[], "Got back an array: " + arr);
+            final int len = ((Object[])arr).length;
+
+            assertEquals(len, 1, "One element in the array " + len);
+
+            int newCnt = Utils.countChildren(KnockoutTest.class, "ul");
+            assertEquals(newCnt, 1, "One child in the DOM: " + newCnt);
+
+            assertEquals(m.getArchetypes().size(), 1, "One archetype: " + m.getArchetypes());
+            assertNotNull(m.getArchetypes().get(0), "Not null: " + m.getArchetypes());
+            assertEquals(m.getArchetypes().get(0).getArtifactId(), "aid", "'aid' == " + m.getArchetypes());
+        } finally {
+            Utils.exposeHTML(KnockoutTest.class, "");
+        }
+    }
+
+    @Function
+    static void call(KnockoutModel m, String data) {
+        m.setName(data);
+        m.setCallbackCount(m.getCallbackCount() + 1);
+    }
+
+    @Function
+    static void removePerson(KnockoutModel model, Person data) {
+        model.setCallbackCount(model.getCallbackCount() + 1);
+        model.getPeople().remove(data);
+    }
+
+
+    @ComputedProperty
+    static String helloMessage(String name) {
+        return "Hello " + name + "!";
+    }
+
+    @ComputedProperty
+    static List<String> cmpResults(List<String> results) {
+        return results;
+    }
+
+    private static void triggerClick(String id) throws Exception {
+        String s = "var id = arguments[0];"
+            + "var e = window.document.getElementById(id);\n "
+            + "if (e.checked) throw 'It should not be checked yet: ' + e;\n "
+            + "var ev = window.document.createEvent('MouseEvents');\n "
+            + "ev.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);\n "
+            + "e.dispatchEvent(ev);\n "
+            + "if (!e.checked) {\n"
+            + "  e.checked = true;\n "
+            + "  e.dispatchEvent(ev);\n "
+            + "}\n";
+        Utils.executeScript(
+            KnockoutTest.class,
+            s, id);
+    }
+    private static void triggerChildClick(String id, int pos) throws Exception {
+        String s =
+            "var id = arguments[0]; var pos = arguments[1];\n" +
+            "var e = window.document.getElementById(id);\n " +
+            "var ev = window.document.createEvent('MouseEvents');\n " +
+            "ev.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);\n " +
+            "var list = e.childNodes;\n" +
+            "var cnt = -1;\n" +
+            "for (var i = 0; i < list.length; i++) {\n" +
+            "  if (list[i].nodeType == 1) cnt++;\n" +
+            "  if (cnt == pos) return list[i].dispatchEvent(ev);\n" +
+            "}\n" +
+            "return null;\n";
+        Utils.executeScript(
+            KnockoutTest.class,
+            s, id, pos);
+    }
+
+    private static String childText(String id, int pos) throws Exception {
+        String s =
+            "var id = arguments[0]; var pos = arguments[1];" +
+            "var e = window.document.getElementById(id);\n" +
+            "var list = e.childNodes;\n" +
+            "var cnt = -1;\n" +
+            "for (var i = 0; i < list.length; i++) {\n" +
+            "  if (list[i].nodeType == 1) cnt++;\n" +
+            "  if (cnt == pos) return list[i].innerHTML;\n" +
+            "}\n" +
+            "return null;\n";
+        return (String)Utils.executeScript(
+            KnockoutTest.class,
+            s, id, pos);
+    }
+
+    private static BrwsrCtx newContext() {
+        return Utils.newContext(KnockoutTest.class);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json-tck/src/main/java/net/java/html/json/tests/MinesTest.java
----------------------------------------------------------------------
diff --git a/json-tck/src/main/java/net/java/html/json/tests/MinesTest.java b/json-tck/src/main/java/net/java/html/json/tests/MinesTest.java
new file mode 100644
index 0000000..a6059b7
--- /dev/null
+++ b/json-tck/src/main/java/net/java/html/json/tests/MinesTest.java
@@ -0,0 +1,356 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json.tests;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import net.java.html.BrwsrCtx;
+import net.java.html.json.ComputedProperty;
+import net.java.html.json.Function;
+import net.java.html.json.Model;
+import net.java.html.json.ModelOperation;
+import net.java.html.json.Models;
+import net.java.html.json.Property;
+import org.netbeans.html.json.tck.KOTest;
+import static net.java.html.json.tests.Utils.assertEquals;
+
+/** Tests model of a mine field and its behavior in the browser.
+ */
+@Model(className = "Mines", targetId = "", properties = {
+    @Property(name = "state", type = MinesTest.GameState.class),
+    @Property(name = "rows", type = Row.class, array = true),
+})
+public final class MinesTest {
+    Mines m;
+    @KOTest public void paintTheGridOnClick() throws Throwable {
+        if (m == null) {
+            BrwsrCtx ctx = Utils.newContext(MinesTest.class);
+            Object exp = Utils.exposeHTML(MinesTest.class, 
+    "            <button id='init' data-bind='click: normalSize'></button>\n" +
+    "            <table>\n" +
+    "                <tbody id='table'>\n" +
+    "                    <!-- ko foreach: rows -->\n" +
+    "                    <tr>\n" +
+    "                        <!-- ko foreach: columns -->\n" +
+    "                        <td data-bind='css: style' >\n" +
+    "                            <div data-bind='text: html'></div>\n" +
+    "                        </td>\n" +
+    "                        <!-- /ko -->\n" +
+    "                    </tr>\n" +
+    "                    <!-- /ko -->\n" +
+    "                </tbody>\n" +
+    "            </table>\n" +
+    ""
+            );
+            m = Models.bind(new Mines(), ctx);
+            m.applyBindings();
+            int cnt = Utils.countChildren(MinesTest.class, "table");
+            assertEquals(cnt, 0, "Table is empty: " + cnt);
+            scheduleClick("init", 100);
+        }
+
+
+        int cnt = Utils.countChildren(MinesTest.class, "table");
+        if (cnt == 0) {
+            throw new InterruptedException();
+        }
+        assertEquals(cnt, 10, "There is ten rows in the table now: " + cnt);
+        
+        Utils.exposeHTML(MinesTest.class, "");
+    }
+    
+    @KOTest public void countAround() throws Exception {
+        Mines mines = new Mines();
+        mines.init(5, 5, 0);
+        mines.getRows().get(0).getColumns().get(0).setMine(true);
+        mines.getRows().get(1).getColumns().get(0).setMine(true);
+        mines.getRows().get(0).getColumns().get(1).setMine(true);
+        
+        int cnt = around(mines, 1, 1);
+        assertEquals(cnt, 3, "There are three mines around. Was: " + cnt);
+    }
+    
+    private static void scheduleClick(String id, int delay) throws Exception {
+        String s = "var id = arguments[0]; var delay = arguments[1];"
+            + "var e = window.document.getElementById(id);\n "
+            + "var f = function() {;\n "
+            + "  var ev = window.document.createEvent('MouseEvents');\n "
+            + "  ev.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);\n "
+            + "  e.dispatchEvent(ev);\n"
+            + "};\n"
+            + "window.setTimeout(f, delay);";
+        Utils.executeScript(
+            MinesTest.class,
+            s, id, delay);
+    }
+    
+    enum GameState {
+        IN_PROGRESS, WON, LOST;
+    }
+    
+    @Model(className = "Row", properties = {
+        @Property(name = "columns", type = Square.class, array = true)
+    })
+    static class RowModel {
+    }
+
+    @Model(className = "Square", properties = {
+        @Property(name = "state", type = SquareType.class),
+        @Property(name = "mine", type = boolean.class)
+    })
+    static class SquareModel {
+        @ComputedProperty static String html(SquareType state) {
+            if (state == null) return "&nbsp;";
+            switch (state) {
+                case EXPLOSION: return "&#x2717;";
+                case UNKNOWN: return "&nbsp;";
+                case DISCOVERED: return "&#x2714;";  
+                case N_0: return "&nbsp;";
+            }
+            return "&#x278" + (state.ordinal() - 1);
+        }
+        
+        @ComputedProperty static String style(SquareType state) {
+            return state == null ? null : state.toString();
+        }
+    }
+    
+    enum SquareType {
+        N_0, N_1, N_2, N_3, N_4, N_5, N_6, N_7, N_8,
+        UNKNOWN, EXPLOSION, DISCOVERED;
+        
+        final boolean isVisible() {
+            return name().startsWith("N_");
+        }
+
+        final SquareType moreBombsAround() {
+            switch (this) {
+                case EXPLOSION:
+                case UNKNOWN:
+                case DISCOVERED:
+                case N_8:
+                    return this;
+            }
+            return values()[ordinal() + 1];
+        }
+    }
+    
+    @ComputedProperty static boolean fieldShowing(GameState state) {
+        return state != null;
+    }
+    
+    @Function static void normalSize(Mines m) {
+        m.init(10, 10, 10);
+    }
+    
+    @ModelOperation static void init(Mines model, int width, int height, int mines) {
+        List<Row> rows = new ArrayList<Row>(height);
+        for (int y = 0; y < height; y++) {
+            Square[] columns = new Square[width];
+            for (int x = 0; x < width; x++) {
+                columns[x] = new Square(SquareType.UNKNOWN, false);
+            }
+            rows.add(new Row(columns));
+        }
+        
+        Random r = new Random();
+        while (mines > 0) {
+            int x = r.nextInt(width);
+            int y = r.nextInt(height);
+            final Square s = rows.get(y).getColumns().get(x);
+            if (s.isMine()) {
+                continue;
+            }
+            s.setMine(true);
+            mines--;
+        }
+
+        model.setState(GameState.IN_PROGRESS);
+        model.getRows().clear();
+        model.getRows().addAll(rows);
+    }
+    
+    @ModelOperation static void computeMines(Mines model) {
+        List<Integer> xBombs = new ArrayList<Integer>();
+        List<Integer> yBombs = new ArrayList<Integer>();
+        final List<Row> rows = model.getRows();
+        boolean emptyHidden = false;
+        SquareType[][] arr = new SquareType[rows.size()][];
+        for (int y = 0; y < rows.size(); y++) {
+            final List<Square> columns = rows.get(y).getColumns();
+            arr[y] = new SquareType[columns.size()];
+            for (int x = 0; x < columns.size(); x++) {
+                Square sq = columns.get(x);
+                if (sq.isMine()) {
+                    xBombs.add(x);
+                    yBombs.add(y);
+                }
+                if (sq.getState().isVisible()) {
+                    arr[y][x] = SquareType.N_0;
+                } else {
+                    if (!sq.isMine()) {
+                        emptyHidden = true;
+                    }
+                }
+            }
+        }
+        for (int i = 0; i < xBombs.size(); i++) {
+            int x = xBombs.get(i);
+            int y = yBombs.get(i);
+            
+            incrementAround(arr, x, y);
+        }
+        for (int y = 0; y < rows.size(); y++) {
+            final List<Square> columns = rows.get(y).getColumns();
+            for (int x = 0; x < columns.size(); x++) {
+                Square sq = columns.get(x);
+                final SquareType newState = arr[y][x];
+                if (newState != null && newState != sq.getState()) {
+                    sq.setState(newState);
+                }
+            }
+        }
+        
+        if (!emptyHidden) {
+            model.setState(GameState.WON);
+            showAllBombs(model, SquareType.DISCOVERED);
+        }
+    }
+    
+    private static void incrementAround(SquareType[][] arr, int x, int y) {
+        incrementAt(arr, x - 1, y - 1);
+        incrementAt(arr, x - 1, y);
+        incrementAt(arr, x - 1, y + 1);
+
+        incrementAt(arr, x + 1, y - 1);
+        incrementAt(arr, x + 1, y);
+        incrementAt(arr, x + 1, y + 1);
+        
+        incrementAt(arr, x, y - 1);
+        incrementAt(arr, x, y + 1);
+    }
+    
+    private static void incrementAt(SquareType[][] arr, int x, int y) {
+        if (y >= 0 && y < arr.length) {
+            SquareType[] r = arr[y];
+            if (x >= 0 && x < r.length) {
+                SquareType sq = r[x];
+                if (sq != null) {
+                    r[x] = sq.moreBombsAround();
+                }
+            }
+        }
+    }
+    
+    static void showAllBombs(Mines model, SquareType state) {
+        for (Row row : model.getRows()) {
+            for (Square square : row.getColumns()) {
+                if (square.isMine()) {
+                    square.setState(state);
+                }
+            }
+        }
+    }
+    
+    private static void expandKnown(Mines model, Square data) {
+        final List<Row> rows = model.getRows();
+        for (int y = 0; y < rows.size(); y++) {
+            final List<Square> columns = rows.get(y).getColumns();
+            for (int x = 0; x < columns.size(); x++) {
+                Square sq = columns.get(x);
+                if (sq == data) {
+                    expandKnown(model, x, y);
+                    return;
+                }
+            }
+        }
+    }
+    private static void expandKnown(Mines model, int x , int y) {
+        if (y < 0 || y >= model.getRows().size()) {
+            return;
+        }
+        final List<Square> columns = model.getRows().get(y).getColumns();
+        if (x < 0 || x >= columns.size()) {
+            return;
+        }
+        final Square sq = columns.get(x);
+        if (sq.getState() == SquareType.UNKNOWN) {
+            int around = around(model, x, y);
+            final SquareType t = SquareType.valueOf("N_" + around);
+            sq.setState(t);
+            if (sq.getState() == SquareType.N_0) {
+                expandKnown(model, x - 1, y - 1);
+                expandKnown(model, x - 1, y);
+                expandKnown(model, x - 1, y + 1);
+                expandKnown(model, x , y - 1);
+                expandKnown(model, x, y + 1);
+                expandKnown(model, x + 1, y - 1);
+                expandKnown(model, x + 1, y);
+                expandKnown(model, x + 1, y + 1);
+            }
+        }
+    }
+    private static int around(Mines model, int x, int y) {
+        return minesAt(model, x - 1, y - 1)
+                + minesAt(model, x - 1, y)
+                + minesAt(model, x - 1, y + 1)
+                + minesAt(model, x, y - 1)
+                + minesAt(model, x, y + 1)
+                + minesAt(model, x + 1, y - 1)
+                + minesAt(model, x + 1, y)
+                + minesAt(model, x + 1, y + 1);
+    }
+
+    private static int minesAt(Mines model, int x, int y) {
+        if (y < 0 || y >= model.getRows().size()) {
+            return 0;
+        }
+        final List<Square> columns = model.getRows().get(y).getColumns();
+        if (x < 0 || x >= columns.size()) {
+            return 0;
+        }
+        Square sq = columns.get(x);
+        return sq.isMine() ? 1 : 0;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json-tck/src/main/java/net/java/html/json/tests/OperationsTest.java
----------------------------------------------------------------------
diff --git a/json-tck/src/main/java/net/java/html/json/tests/OperationsTest.java b/json-tck/src/main/java/net/java/html/json/tests/OperationsTest.java
new file mode 100644
index 0000000..dfb1951
--- /dev/null
+++ b/json-tck/src/main/java/net/java/html/json/tests/OperationsTest.java
@@ -0,0 +1,96 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json.tests;
+
+import net.java.html.json.Models;
+import org.netbeans.html.json.tck.KOTest;
+import static net.java.html.json.tests.Utils.assertEquals;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class OperationsTest {
+    private JSONik js;
+    private Person p;
+    
+    @KOTest public void syncOperation() {
+        js = Models.bind(new JSONik(), Utils.newContext(OperationsTest.class));
+        p = new Person("Sitar", "Gitar", Sex.MALE, null);
+        js.applyBindings();
+        js.setFetched(p);
+        Person p = js.getFetched();
+        assertEquals("Sitar", p.getFirstName(), "Expecting Sitar immediately: " + p.getFirstName());
+        assertEquals(Sex.MALE, p.getSex(), "Expecting MALE immediately: " + p.getSex());
+    }
+    
+    
+    @KOTest public void asyncOperation() throws InterruptedException {
+        if (js == null) {
+            if (Utils.skipIfNoFullJDK()) {
+                return;
+            }
+            
+            
+            js = Models.bind(new JSONik(), Utils.newContext(OperationsTest.class));
+            p = new Person("Sitar", "Gitar", Sex.MALE, null);
+            js.applyBindings();
+
+            js.setFetched(null);
+            new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    js.assignFetched(p);
+                }
+            }).start();
+        }
+    
+        Person p = js.getFetched();
+        if (p == null) {
+            throw new InterruptedException();
+        }
+        
+        assertEquals("Sitar", p.getFirstName(), "Expecting Sitar: " + p.getFirstName());
+        assertEquals(Sex.MALE, p.getSex(), "Expecting MALE: " + p.getSex());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json-tck/src/main/java/net/java/html/json/tests/PairModel.java
----------------------------------------------------------------------
diff --git a/json-tck/src/main/java/net/java/html/json/tests/PairModel.java b/json-tck/src/main/java/net/java/html/json/tests/PairModel.java
new file mode 100644
index 0000000..20843ec
--- /dev/null
+++ b/json-tck/src/main/java/net/java/html/json/tests/PairModel.java
@@ -0,0 +1,80 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json.tests;
+
+import java.util.Arrays;
+import java.util.List;
+import net.java.html.BrwsrCtx;
+import net.java.html.json.ComputedProperty;
+import net.java.html.json.Function;
+import net.java.html.json.Model;
+import net.java.html.json.Property;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+@Model(className = "Pair", targetId = "", properties = {
+    @Property(name = "firstName", type = String.class),
+    @Property(name = "lastName", type = String.class),
+    @Property(name = "next", type = Pair.class)
+})
+class PairModel {
+    static BrwsrCtx ctx;
+    
+    @ComputedProperty 
+    static List<String> bothNames(String firstName, String lastName) {
+        return Arrays.asList(firstName, lastName);
+    }
+    
+    @ComputedProperty
+    static Pair nextOne(Pair next) {
+        return next;
+    }
+    
+    @Function 
+    static void assignFirstName(Pair m, String data) {
+        ctx = BrwsrCtx.findDefault(Pair.class);
+        m.setFirstName(data);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json-tck/src/main/java/net/java/html/json/tests/PersonImpl.java
----------------------------------------------------------------------
diff --git a/json-tck/src/main/java/net/java/html/json/tests/PersonImpl.java b/json-tck/src/main/java/net/java/html/json/tests/PersonImpl.java
new file mode 100644
index 0000000..e7f8ede
--- /dev/null
+++ b/json-tck/src/main/java/net/java/html/json/tests/PersonImpl.java
@@ -0,0 +1,100 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json.tests;
+
+import net.java.html.json.ComputedProperty;
+import net.java.html.json.Function;
+import net.java.html.json.Model;
+import net.java.html.json.Property;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+@Model(className = "Person", properties = {
+    @Property(name = "firstName", type = String.class),
+    @Property(name = "lastName", type = String.class),
+    @Property(name = "sex", type = Sex.class),
+    @Property(name = "address", type = Address.class)
+})
+final class PersonImpl {
+    @ComputedProperty(write = "parseNames")
+    public static String fullName(String firstName, String lastName) {
+        return firstName + " " + lastName;
+    }
+
+    static void parseNames(Person p, String fullName) {
+        String[] arr = fullName.split(" ");
+        p.setFirstName(arr[0]);
+        p.setLastName(arr[1]);
+    }
+    
+    @ComputedProperty
+    public static String sexType(Sex sex) {
+        return sex == null ? "unknown" : sex.toString();
+    }
+    
+    @Function
+    static void changeSex(Person p) {
+        if (p.getSex() == Sex.MALE) {
+            p.setSex(Sex.FEMALE);
+        } else {
+            p.setSex(Sex.MALE);
+        }
+    }
+    
+    @Model(className = "People", properties = {
+        @Property(array = true, name = "info", type = Person.class),
+        @Property(array = true, name = "nicknames", type = String.class),
+        @Property(array = true, name = "age", type = int.class),
+        @Property(array = true, name = "sex", type = Sex.class)
+    })
+    public class PeopleImpl {
+    }
+    
+    @Model(className = "Address", properties = {
+        @Property(name = "street", type = String.class)
+    })
+    static class Addrss {
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json-tck/src/main/java/net/java/html/json/tests/Sex.java
----------------------------------------------------------------------
diff --git a/json-tck/src/main/java/net/java/html/json/tests/Sex.java b/json-tck/src/main/java/net/java/html/json/tests/Sex.java
new file mode 100644
index 0000000..2a266d7
--- /dev/null
+++ b/json-tck/src/main/java/net/java/html/json/tests/Sex.java
@@ -0,0 +1,51 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json.tests;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public enum Sex {
+    MALE, FEMALE;
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json-tck/src/main/java/net/java/html/json/tests/Utils.java
----------------------------------------------------------------------
diff --git a/json-tck/src/main/java/net/java/html/json/tests/Utils.java b/json-tck/src/main/java/net/java/html/json/tests/Utils.java
new file mode 100644
index 0000000..ddaf8ec
--- /dev/null
+++ b/json-tck/src/main/java/net/java/html/json/tests/Utils.java
@@ -0,0 +1,225 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json.tests;
+
+import java.net.URI;
+import java.util.Collections;
+import java.util.Map;
+import java.util.ServiceLoader;
+import net.java.html.BrwsrCtx;
+import org.netbeans.html.json.tck.KnockoutTCK;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public final class Utils {
+    private static KnockoutTCK instantiatedTCK;
+
+    static boolean skipIfNoFullJDK() {
+        try {
+            Class<?> thread = Class.forName("java.lang.Thread");
+            Thread t = new Thread("Empty");
+            t.setName("Different");
+            t.setDaemon(false);
+            t.interrupt();
+            t.start();
+        } catch (ClassNotFoundException ex) {
+            return true;
+        } catch (SecurityException ex) {
+            return true;
+        }
+        return false;
+    }
+
+    private Utils() {
+    }
+    
+    public static void registerTCK(KnockoutTCK tck) {
+        instantiatedTCK = tck;
+    }
+
+    static  BrwsrCtx newContext(Class<?> clazz) {
+        for (KnockoutTCK tck : tcks(clazz)) {
+            BrwsrCtx c = tck.createContext();
+            if (c != null) {
+                return c;
+            }
+        }
+        throw new AssertionError("Can't find appropriate Context in ServiceLoader!");
+    }
+    static Object createObject(Map<String,Object> values, Class<?> clazz) {
+        for (KnockoutTCK tck : tcks(clazz)) {
+            Object o = tck.createJSON(values);
+            if (o != null) {
+                return o;
+            }
+        }
+        throw new AssertionError("Can't find appropriate Context in ServiceLoader!");
+    }
+    static Object executeScript(Class<?> clazz, 
+        String script, Object... arguments
+    ) throws Exception {
+        for (KnockoutTCK tck : tcks(clazz)) {
+            return tck.executeScript(script, arguments);
+        }
+        throw new AssertionError("Can't find appropriate Context in ServiceLoader!");
+    }
+
+    private static Iterable<KnockoutTCK> tcks(Class<?> clazz) {
+        if (instantiatedTCK != null) {
+            return Collections.singleton(instantiatedTCK);
+        }
+        return ServiceLoader.load(KnockoutTCK.class, cl(clazz));
+    }
+    
+    static Object exposeHTML(Class<?> clazz, String html) throws Exception {
+        String s = 
+          "var n = window.document.getElementById('ko.test.div'); \n "
+        + "if (!n) { \n"
+        + "  n = window.document.createElement('div'); \n "
+        + "  n.id = 'ko.test.div'; \n "
+        + "  var body = window.document.getElementsByTagName('body')[0];\n"
+        + "  body.appendChild(n);\n"
+        + "}\n"
+        + "n.innerHTML = arguments[0]; \n ";
+        return executeScript(clazz, s, html);
+    }
+
+    static int countChildren(Class<?> caller, String id) throws Exception {
+        return ((Number) executeScript(caller, 
+            "var e = window.document.getElementById(arguments[0]);\n" + 
+            "if (typeof e === 'undefined') return -2;\n " + 
+            "var list = e.childNodes;\n" +
+            "var cnt = 0;\n" + 
+            "for (var i = 0; i < list.length; i++) {\n" + 
+            "  if (list[i].nodeType == 1) cnt++;\n" + 
+            "}\n" + 
+            "return cnt;\n"
+            , id
+        )).intValue();
+    }
+
+    static Object addChildren(Class<?> caller, String id, String field, Object value) throws Exception {
+        return executeScript(caller, 
+            "var e = window.document.getElementById(arguments[0]);\n" + 
+            "var f = arguments[1];\n" + 
+            "var v = arguments[2];\n" + 
+            "if (typeof e === 'undefined') return -2;\n " + 
+            "var c = ko.contextFor(e);\n" +
+            "var fn = c.$rawData[f];\n" +
+            "var arr = c.$rawData[f]();\n" +
+            "arr.push(v);\n" + 
+            "fn(arr);\n" + 
+            "return arr;\n"
+            , id, field, value
+        );
+    }
+    
+    static String prepareURL(
+        Class<?> clazz, String content, String mimeType, String... parameters) {
+        for (KnockoutTCK tck : tcks(clazz)) {
+            URI o = tck.prepareURL(content, mimeType, parameters);
+            if (o != null) {
+                return o.toString();
+            }
+        }
+        throw new IllegalStateException();
+    }
+
+    static boolean canFailWebSockets(
+        Class<?> clazz) {
+        for (KnockoutTCK tck : tcks(clazz)) {
+            if (tck.canFailWebSocketTest()) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    private static ClassLoader cl(Class<?> c) {
+        try {
+            return c.getClassLoader();
+        } catch (SecurityException ex) {
+            return null;
+        }
+    }
+    
+    static void fail(String msg) {
+        throw new AssertionError(msg);
+    }
+    
+    static void assertTrue(boolean c, String msg) {
+        if (!c) {
+            throw new AssertionError(msg);
+        }
+    }
+
+    static void assertFalse(boolean c, String msg) {
+        if (c) {
+            throw new AssertionError(msg);
+        }
+    }
+    
+    static void assertNull(Object o, String msg) {
+        if (o != null) {
+            throw new AssertionError(msg + " but was: " + o);
+        }
+    }
+
+    static void assertNotNull(Object o, String msg) {
+        if (o == null) {
+            throw new AssertionError(msg);
+        }
+    }
+    
+    static void assertEquals(Object a, Object b, String msg) {
+        if (a == b) {
+            return;
+        }
+        if (a != null && a.equals(b)) {
+            return;
+        }
+        throw new AssertionError(msg + " expecting: " + b + " actual: " + a);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json-tck/src/main/java/net/java/html/json/tests/WebSocketTest.java
----------------------------------------------------------------------
diff --git a/json-tck/src/main/java/net/java/html/json/tests/WebSocketTest.java b/json-tck/src/main/java/net/java/html/json/tests/WebSocketTest.java
new file mode 100644
index 0000000..267438d
--- /dev/null
+++ b/json-tck/src/main/java/net/java/html/json/tests/WebSocketTest.java
@@ -0,0 +1,179 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json.tests;
+
+import net.java.html.BrwsrCtx;
+import net.java.html.json.Model;
+import net.java.html.json.Models;
+import net.java.html.json.OnReceive;
+import net.java.html.json.Property;
+import org.netbeans.html.json.tck.KOTest;
+import static net.java.html.json.tests.Utils.assertEquals;
+import static net.java.html.json.tests.Utils.assertNotNull;
+import static net.java.html.json.tests.Utils.assertTrue;
+import static net.java.html.json.tests.Utils.fail;
+
+/** Testing support of WebSocket communication.
+ *
+ * @author Jaroslav Tulach
+ */
+@Model(className = "WebSocketik", targetId="", properties = {
+    @Property(name = "fetched", type = Person.class),
+    @Property(name = "fetchedCount", type = int.class),
+    @Property(name = "open", type = int.class),
+    @Property(name = "fetchedResponse", type = String.class),
+    @Property(name = "fetchedSex", type = Sex.class, array = true)
+})
+public final class WebSocketTest {
+    private WebSocketik js;
+    private String url;
+    
+    @OnReceive(url = "{url}", data = Sex.class, method = "WebSocket", onError = "error")
+    static void querySex(WebSocketik model, Person data) {
+        if (data == null) {
+            model.setOpen(1);
+        } else {
+            model.setFetched(data);
+        }
+    }
+    
+    @KOTest public void connectUsingWebSocket() throws Throwable {
+        if (js == null) {
+            url = Utils.prepareURL(
+                JSONTest.class, "{'firstName': 'Mitar', 'sex': '$0' }", 
+                "application/javascript",
+                "protocol:ws"
+            );
+            
+            js = Models.bind(new WebSocketik(), newContext());
+            js.applyBindings();
+
+            js.setFetched(null);
+            
+            // connects to the server
+            js.querySex(url, null);
+        }
+        
+        if (bailOutIfNotSupported(js)) {
+            return;
+        }
+        
+        if (js.getOpen() == 0) {
+            throw new InterruptedException();
+        }
+        if (js.getOpen() == 1) {
+            // send a query to the server
+            js.querySex(url, Sex.FEMALE);
+            js.setOpen(2);
+        }
+    
+        Person p = js.getFetched();
+        if (p == null) {
+            throw new InterruptedException();
+        }
+        
+        assertEquals("Mitar", p.getFirstName(), "Unexpected: " + p.getFirstName());
+        assertEquals(Sex.FEMALE, p.getSex(), "Expecting FEMALE: " + p.getSex());
+
+        if (js.getOpen() == 2) {
+            // close the socket
+            js.querySex(url, null);
+            js.setOpen(3);
+        }
+        
+        if (js.getFetchedResponse() == null) {
+            throw new InterruptedException();
+        }
+        assertEquals("null", js.getFetchedResponse(), "Should be null: " + js.getFetchedResponse());
+    }
+    
+    @KOTest public void errorUsingWebSocket() throws Throwable {
+        if (js == null) {
+            js = Models.bind(new WebSocketik(), newContext());
+            js.applyBindings();
+
+            js.setFetched(null);
+            js.querySex("http://wrong.protocol", null);
+        }
+
+        if (js.getFetchedResponse() == null) {
+            throw new InterruptedException();
+        }
+
+        assertNotNull(js.getFetchedResponse(), "Error reported");
+    }
+
+    @KOTest public void haveToOpenTheWebSocket() throws Throwable {
+        js = Models.bind(new WebSocketik(), newContext());
+        js.applyBindings();
+
+        js.setFetched(null);
+        try {
+            js.querySex("http://wrong.protocol", Sex.MALE);
+            fail("Should throw an exception");
+        } catch (IllegalStateException ex) {
+            assertTrue(ex.getMessage().contains("not open"), "Expecting 'not open' msg: " + ex.getMessage());
+        }
+    }
+    
+    static void error(WebSocketik model, Exception ex) {
+        if (ex != null) {
+            model.setFetchedResponse(ex.getClass() + ":" + ex.getMessage());
+        } else {
+            model.setFetchedResponse("null");
+        }
+    }
+    
+    private static BrwsrCtx newContext() {
+        return Utils.newContext(WebSocketTest.class);
+    }
+
+    private boolean bailOutIfNotSupported(WebSocketik js) {
+        if (js.getFetchedResponse() == null) {
+            return false;
+        }
+        return js.getFetchedResponse().contains("UnsupportedOperationException") &&
+            Utils.canFailWebSockets(WebSocketTest.class);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json-tck/src/main/java/org/netbeans/html/json/tck/JavaScriptTCK.java
----------------------------------------------------------------------
diff --git a/json-tck/src/main/java/org/netbeans/html/json/tck/JavaScriptTCK.java b/json-tck/src/main/java/org/netbeans/html/json/tck/JavaScriptTCK.java
new file mode 100644
index 0000000..755eb1c
--- /dev/null
+++ b/json-tck/src/main/java/org/netbeans/html/json/tck/JavaScriptTCK.java
@@ -0,0 +1,73 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.json.tck;
+
+import net.java.html.js.tests.GCBodyTest;
+import net.java.html.js.tests.JavaScriptBodyTest;
+import org.netbeans.html.boot.spi.Fn;
+import org.netbeans.html.boot.spi.Fn.Presenter;
+
+/** Entry point for those who want to verify that their implementation of
+ * {@link Presenter} is good enough to support existing Java/JavaScript 
+ * communication use-cases. Subclass this class, get list of {@link #testClasses() classes}
+ * find methods annotated by {@link KOTest} annotation and execute them.
+ * <p>
+ *
+ * @author Jaroslav Tulach
+ * @since 0.7
+ */
+public abstract class JavaScriptTCK {
+    /** Gives you list of classes included in the TCK. Their test methods
+     * are annotated by {@link KOTest} annotation. The methods are public
+     * instance methods that take no arguments. The method should be 
+     * invoke in a presenter context {@link Fn#activate(org.netbeans.html.boot.spi.Fn.Presenter)}.
+     * 
+     * @return classes with methods annotated by {@link KOTest} annotation
+     */
+    protected static Class<?>[] testClasses() {
+        return new Class[] { 
+            JavaScriptBodyTest.class, GCBodyTest.class
+        };
+    }
+    
+}



[04/24] incubator-netbeans-html4j git commit: [INFRA-15006] Initial donation of HTML/Java NetBeans API. Equivalent to the content of html4j-donation-review.zip donated as part of ApacheNetBeansDonation1.zip with SHA256 being 7f2ca0f61953a190613c9a0fbcc1b

Posted by jt...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko-osgi-test/pom.xml
----------------------------------------------------------------------
diff --git a/ko-osgi-test/pom.xml b/ko-osgi-test/pom.xml
new file mode 100644
index 0000000..2af6772
--- /dev/null
+++ b/ko-osgi-test/pom.xml
@@ -0,0 +1,181 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2016 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<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">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.netbeans.html</groupId>
+        <artifactId>pom</artifactId>
+        <version>2.0-SNAPSHOT</version>
+    </parent>
+    <name>KO Tests in Equinox OSGi Container</name>
+    <artifactId>ko-osgi-test</artifactId>
+    <packaging>bundle</packaging>
+    <description>Runs the TCK for Knockout in Equinox OSGi Container</description>
+    <properties>
+        <netbeans.compile.on.save>none</netbeans.compile.on.save>
+    </properties>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.netbeans.html</groupId>
+                <artifactId>html4j-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-deploy-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-failsafe-plugin</artifactId>
+                <configuration>
+                    <additionalClasspathElements>
+                        <additionalClasspathElement>${project.build.directory}/${project.build.finalName}.jar</additionalClasspathElement>
+                    </additionalClasspathElements>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>integration-test</goal>
+                            <goal>verify</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+    <dependencies>
+        <dependency>
+            <groupId>com.oracle</groupId>
+            <artifactId>javafx</artifactId>
+            <version>2.2</version>
+            <scope>system</scope>
+            <systemPath>${jfxrt.jar}</systemPath>
+        </dependency>
+        <dependency>
+            <groupId>de.twentyeleven.skysail</groupId>
+            <artifactId>org.json-osgi</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.netbeans.html</groupId>
+            <artifactId>net.java.html.json</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.testng</groupId>
+            <artifactId>testng</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>net.java.html.json.tck</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.netbeans.api</groupId>
+            <artifactId>org-openide-util-lookup</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.netbeans.html</groupId>
+            <artifactId>net.java.html.boot</artifactId>
+            <version>${project.version}</version>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ko4j</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>net.java.html.boot.fx</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.grizzly</groupId>
+            <artifactId>grizzly-http-server</artifactId>
+            <version>${grizzly.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.grizzly</groupId>
+            <artifactId>grizzly-websockets-server</artifactId>
+            <version>${grizzly.version}</version>
+            <scope>test</scope>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.grizzly</groupId>
+            <artifactId>grizzly-http-servlet</artifactId>
+            <version>${grizzly.version}</version>
+            <scope>test</scope>
+        </dependency>    
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <scope>test</scope>
+            <version>3.1.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse</groupId>
+            <artifactId>org.eclipse.osgi</artifactId>
+            <version>3.8.0.v20120529-1548</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>equinox-agentclass-hook</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko-osgi-test/src/main/java/org/netbeans/html/ko/osgi/test/KnockoutEquinoxTCKImpl.java
----------------------------------------------------------------------
diff --git a/ko-osgi-test/src/main/java/org/netbeans/html/ko/osgi/test/KnockoutEquinoxTCKImpl.java b/ko-osgi-test/src/main/java/org/netbeans/html/ko/osgi/test/KnockoutEquinoxTCKImpl.java
new file mode 100644
index 0000000..1ff2564
--- /dev/null
+++ b/ko-osgi-test/src/main/java/org/netbeans/html/ko/osgi/test/KnockoutEquinoxTCKImpl.java
@@ -0,0 +1,222 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.ko.osgi.test;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import net.java.html.BrwsrCtx;
+import net.java.html.boot.BrowserBuilder;
+import net.java.html.js.JavaScriptBody;
+import org.netbeans.html.boot.spi.Fn;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.json.spi.Technology;
+import org.netbeans.html.json.spi.Transfer;
+import org.netbeans.html.json.tck.KnockoutTCK;
+import org.openide.util.lookup.ServiceProvider;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+@ServiceProvider(service = KnockoutTCK.class)
+public class KnockoutEquinoxTCKImpl extends KnockoutTCK implements Callable<Class[]> {
+    
+    private static Fn.Presenter browserContext;
+
+    public static Class loadOSGiClass(String name, BundleContext ctx) throws Exception {
+        for (Bundle b : ctx.getBundles()) {
+            try {
+                Class<?> osgiClass = b.loadClass(name);
+                if (osgiClass != null && osgiClass.getClassLoader() != ClassLoader.getSystemClassLoader()) {
+                    return osgiClass;
+                }
+            } catch (ClassNotFoundException cnfe) {
+                // go on
+            }
+        }
+        throw new IllegalStateException("Cannot load " + name + " from the OSGi container!");
+    }
+
+    @Override
+    public Class[] call() throws Exception {
+        return testClasses();
+    }
+    
+    public static void start(URI server) throws Exception {
+        final BrowserBuilder bb = BrowserBuilder.newBrowser().loadClass(KnockoutEquinoxTCKImpl.class).
+            loadPage(server.toString()).
+            invoke("initialized");
+
+        Executors.newSingleThreadExecutor().submit(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    final ClassLoader osgiClassLoader = BrowserBuilder.class.getClassLoader();
+                    bb.classloader(osgiClassLoader);
+                    bb.showAndWait();
+                } catch (Throwable t) {
+                    t.printStackTrace();
+                }
+            }
+        });
+    }
+
+    public static void initialized() throws Exception {
+        Bundle bundle = FrameworkUtil.getBundle(KnockoutEquinoxTCKImpl.class);
+        if (bundle == null) {
+            throw new IllegalStateException(
+                "Should be loaded from a bundle. But was: " + KnockoutEquinoxTCKImpl.class.getClassLoader()
+            );
+        }
+        Class<?> classpathClass = ClassLoader.getSystemClassLoader().loadClass(
+            "org.netbeans.html.ko.osgi.test.KnockoutEquinoxIT"
+        );
+        Method m = classpathClass.getMethod("initialized", Class.class, Object.class);
+        browserContext = Fn.activePresenter();
+        m.invoke(null, KnockoutEquinoxTCKImpl.class, browserContext);
+    }
+    
+    @Override
+    public BrwsrCtx createContext() {
+        try {
+            Contexts.Builder cb = Contexts.newBuilder().
+                register(Technology.class, (Technology)osgiInstance("KOTech"), 10).
+                register(Transfer.class, (Transfer)osgiInstance("KOTransfer"), 10).
+                register(Executor.class, (Executor)browserContext, 10);
+//        if (fx.areWebSocketsSupported()) {
+//            cb.register(WSTransfer.class, fx, 10);
+//        }
+            return cb.build();
+        } catch (Exception ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+    private Object osgiInstance(String simpleName) throws IllegalAccessException, SecurityException, IllegalArgumentException, Exception, NoSuchMethodException, InstantiationException, InvocationTargetException {
+        Class<?> fxCls = loadOSGiClass(
+                "org.netbeans.html.ko4j." + simpleName,
+                FrameworkUtil.getBundle(KnockoutEquinoxTCKImpl.class).getBundleContext()
+        );
+        final Constructor<?> cnstr = fxCls.getDeclaredConstructor();
+        cnstr.setAccessible(true);
+        Object fx = cnstr.newInstance();
+        return fx;
+    }
+
+    @Override
+    public Object createJSON(Map<String, Object> values) {
+        Object json = createObj();
+        for (Map.Entry<String, Object> entry : values.entrySet()) {
+            putObj(json, entry.getKey(), entry.getValue());
+        }
+        return json;
+    }
+
+    @JavaScriptBody(args = {}, body = "return {};")
+    private static native Object createObj();
+
+    @JavaScriptBody(args = {"obj", "prop", "val"}, body = "obj[prop] = val;")
+    private static native void putObj(Object obj, String prop, Object val);
+
+    @Override
+    @JavaScriptBody(args = { "s", "args" }, body = ""
+        + "var f = new Function(s); "
+        + "return f.apply(null, args);"
+    )
+    public native Object executeScript(String script, Object[] arguments);
+
+    @JavaScriptBody(args = {  }, body = 
+          "var h;"
+        + "if (!!window && !!window.location && !!window.location.href)\n"
+        + "  h = window.location.href;\n"
+        + "else "
+        + "  h = null;"
+        + "return h;\n"
+    )
+    private static native String findBaseURL();
+    
+    @Override
+    public URI prepareURL(String content, String mimeType, String[] parameters) {
+        try {
+            final URL baseURL = new URL(findBaseURL());
+            StringBuilder sb = new StringBuilder();
+            sb.append("/dynamic?mimeType=").append(mimeType);
+            for (int i = 0; i < parameters.length; i++) {
+                sb.append("&param" + i).append("=").append(parameters[i]);
+            }
+            String mangle = content.replace("\n", "%0a")
+                .replace("\"", "\\\"").replace(" ", "%20");
+            sb.append("&content=").append(mangle);
+
+            URL query = new URL(baseURL, sb.toString());
+            URLConnection c = query.openConnection();
+            BufferedReader br = new BufferedReader(new InputStreamReader(c.getInputStream()));
+            URI connectTo = new URI(br.readLine());
+            return connectTo;
+        } catch (IOException ex) {
+            throw new IllegalStateException(ex);
+        } catch (URISyntaxException ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+
+    @Override
+    public boolean canFailWebSocketTest() {
+        return true;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko-osgi-test/src/test/java/org/netbeans/html/ko/osgi/test/DynamicHTTP.java
----------------------------------------------------------------------
diff --git a/ko-osgi-test/src/test/java/org/netbeans/html/ko/osgi/test/DynamicHTTP.java b/ko-osgi-test/src/test/java/org/netbeans/html/ko/osgi/test/DynamicHTTP.java
new file mode 100644
index 0000000..3c70570
--- /dev/null
+++ b/ko-osgi-test/src/test/java/org/netbeans/html/ko/osgi/test/DynamicHTTP.java
@@ -0,0 +1,261 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.ko.osgi.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.glassfish.grizzly.PortRange;
+import org.glassfish.grizzly.http.server.HttpHandler;
+import org.glassfish.grizzly.http.server.HttpServer;
+import org.glassfish.grizzly.http.server.NetworkListener;
+import org.glassfish.grizzly.http.server.Request;
+import org.glassfish.grizzly.http.server.Response;
+import org.glassfish.grizzly.http.server.ServerConfiguration;
+import org.glassfish.grizzly.websockets.WebSocket;
+import org.glassfish.grizzly.websockets.WebSocketAddOn;
+import org.glassfish.grizzly.websockets.WebSocketApplication;
+import org.glassfish.grizzly.websockets.WebSocketEngine;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+final class DynamicHTTP extends HttpHandler {
+    private static final Logger LOG = Logger.getLogger(DynamicHTTP.class.getName());
+    private static int resourcesCount;
+    private static List<Resource> resources;
+    private static ServerConfiguration conf;
+    private static HttpServer server;
+
+    private DynamicHTTP() {
+    }
+
+    static URI initServer() throws Exception {
+        server = HttpServer.createSimpleServer(null, new PortRange(8080, 65535));
+        final WebSocketAddOn addon = new WebSocketAddOn();
+        for (NetworkListener listener : server.getListeners()) {
+            listener.registerAddOn(addon);
+        }
+        resources = new ArrayList<Resource>();
+
+        conf = server.getServerConfiguration();
+        final DynamicHTTP dh = new DynamicHTTP();
+
+        conf.addHttpHandler(dh, "/");
+
+        server.start();
+
+        return pageURL("http", server, "/test.html");
+    }
+
+    @Override
+    public void service(Request request, Response response) throws Exception {
+        if ("/test.html".equals(request.getRequestURI())) {
+            response.setContentType("text/html");
+            final InputStream is = DynamicHTTP.class.getResourceAsStream("test.html");
+            copyStream(is, response.getOutputStream(), null);
+            return;
+        }
+        if ("/dynamic".equals(request.getRequestURI())) {
+            String mimeType = request.getParameter("mimeType");
+            List<String> params = new ArrayList<String>();
+            boolean webSocket = false;
+            for (int i = 0;; i++) {
+                String p = request.getParameter("param" + i);
+                if (p == null) {
+                    break;
+                }
+                if ("protocol:ws".equals(p)) {
+                    webSocket = true;
+                    continue;
+                }
+                params.add(p);
+            }
+            final String cnt = request.getParameter("content");
+            String mangle = cnt.replace("%20", " ").replace("%0A", "\n");
+            ByteArrayInputStream is = new ByteArrayInputStream(mangle.getBytes("UTF-8"));
+            URI url;
+            final Resource res = new Resource(is, mimeType, "/dynamic/res" + ++resourcesCount, params.toArray(new String[params.size()]));
+            if (webSocket) {
+                url = registerWebSocket(res);
+            } else {
+                url = registerResource(res);
+            }
+            response.getWriter().write(url.toString());
+            response.getWriter().write("\n");
+            return;
+        }
+
+        for (Resource r : resources) {
+            if (r.httpPath.equals(request.getRequestURI())) {
+                response.setContentType(r.httpType);
+                r.httpContent.reset();
+                String[] params = null;
+                if (r.parameters.length != 0) {
+                    params = new String[r.parameters.length];
+                    for (int i = 0; i < r.parameters.length; i++) {
+                        params[i] = request.getParameter(r.parameters[i]);
+                        if (params[i] == null) {
+                            if ("http.method".equals(r.parameters[i])) {
+                                params[i] = request.getMethod().toString();
+                            } else if ("http.requestBody".equals(r.parameters[i])) {
+                                Reader rdr = request.getReader();
+                                StringBuilder sb = new StringBuilder();
+                                for (;;) {
+                                    int ch = rdr.read();
+                                    if (ch == -1) {
+                                        break;
+                                    }
+                                    sb.append((char) ch);
+                                }
+                                params[i] = sb.toString();
+                            } else if (r.parameters[i].startsWith("http.header.")) {
+                                params[i] = request.getHeader(r.parameters[i].substring(12));
+                            }
+                        }
+                        if (params[i] == null) {
+                            params[i] = "null";
+                        }
+                    }
+                }
+
+                copyStream(r.httpContent, response.getOutputStream(), null, params);
+            }
+        }
+    }
+
+    private URI registerWebSocket(Resource r) {
+        WebSocketEngine.getEngine().register("", r.httpPath, new WS(r));
+        return pageURL("ws", server, r.httpPath);
+    }
+
+    private URI registerResource(Resource r) {
+        if (!resources.contains(r)) {
+            resources.add(r);
+            conf.addHttpHandler(this, r.httpPath);
+        }
+        return pageURL("http", server, r.httpPath);
+    }
+
+    private static URI pageURL(String proto, HttpServer server, final String page) {
+        NetworkListener listener = server.getListeners().iterator().next();
+        int port = listener.getPort();
+        try {
+            return new URI(proto + "://localhost:" + port + page);
+        } catch (URISyntaxException ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+
+    static final class Resource {
+
+        final InputStream httpContent;
+        final String httpType;
+        final String httpPath;
+        final String[] parameters;
+
+        Resource(InputStream httpContent, String httpType, String httpPath,
+            String[] parameters) {
+            httpContent.mark(Integer.MAX_VALUE);
+            this.httpContent = httpContent;
+            this.httpType = httpType;
+            this.httpPath = httpPath;
+            this.parameters = parameters;
+        }
+    }
+
+    static void copyStream(InputStream is, OutputStream os, String baseURL, String... params) throws IOException {
+        for (;;) {
+            int ch = is.read();
+            if (ch == -1) {
+                break;
+            }
+            if (ch == '$' && params.length > 0) {
+                int cnt = is.read() - '0';
+                if (baseURL != null && cnt == 'U' - '0') {
+                    os.write(baseURL.getBytes("UTF-8"));
+                } else {
+                    if (cnt >= 0 && cnt < params.length) {
+                        os.write(params[cnt].getBytes("UTF-8"));
+                    } else {
+                        os.write('$');
+                        os.write(cnt + '0');
+                    }
+                }
+            } else {
+                os.write(ch);
+            }
+        }
+    }
+
+    private static class WS extends WebSocketApplication {
+        private final Resource r;
+
+        private WS(Resource r) {
+            this.r = r;
+        }
+
+        @Override
+        public void onMessage(WebSocket socket, String text) {
+            try {
+                r.httpContent.reset();
+                ByteArrayOutputStream out = new ByteArrayOutputStream();
+                copyStream(r.httpContent, out, null, text);
+                String s = new String(out.toByteArray(), "UTF-8");
+                socket.send(s);
+            } catch (IOException ex) {
+                LOG.log(Level.WARNING, "Error processing message " + text, ex);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko-osgi-test/src/test/java/org/netbeans/html/ko/osgi/test/KOFx.java
----------------------------------------------------------------------
diff --git a/ko-osgi-test/src/test/java/org/netbeans/html/ko/osgi/test/KOFx.java b/ko-osgi-test/src/test/java/org/netbeans/html/ko/osgi/test/KOFx.java
new file mode 100644
index 0000000..686a930
--- /dev/null
+++ b/ko-osgi-test/src/test/java/org/netbeans/html/ko/osgi/test/KOFx.java
@@ -0,0 +1,130 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.ko.osgi.test;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import javafx.application.Platform;
+import org.testng.ITest;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public final class KOFx implements ITest, Runnable {
+    private final Object p;
+    private final Method m;
+    private Object result;
+    private Object inst;
+    private int count;
+
+    KOFx(Object p, Method m) {
+        this.p = p;
+        this.m = m;
+    }
+
+    @Override
+    public String getTestName() {
+        return m.getName();
+    }
+
+    @Test
+    public synchronized void executeTest() throws Exception {
+        if (result == null) {
+            Platform.runLater(this);
+            wait();
+        }
+        if (result instanceof Exception) {
+            throw (Exception)result;
+        }
+        if (result instanceof Error) {
+            throw (Error)result;
+        }
+    }
+
+    @Override
+    public synchronized void run() {
+        boolean notify = true;
+        Closeable a = null;
+        try {
+            a = KnockoutEquinoxIT.activateInOSGi(p);
+            if (inst == null) {
+                inst = m.getDeclaringClass().newInstance();
+            }
+            result = m.invoke(inst);
+            if (result == null) {
+                result = this;
+            }
+        } catch (InvocationTargetException ex) {
+            Throwable r = ex.getTargetException();
+            if (r instanceof InterruptedException) {
+                if (count++ < 10000) {
+                    notify = false;
+                    try {
+                        Thread.sleep(100);
+                    } catch (Exception ex1) {
+                        // ignore and continue
+                    }
+                    Platform.runLater(this);
+                    return;
+                }
+            }
+            result = r;
+        } catch (Exception ex) {
+            result = ex;
+        } finally {
+            if (notify) {
+                notifyAll();
+            }
+            try {
+                if (a != null) a.close();
+            } catch (IOException ex) {
+                throw new IllegalStateException(ex);
+            }
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko-osgi-test/src/test/java/org/netbeans/html/ko/osgi/test/KnockoutEquinoxIT.java
----------------------------------------------------------------------
diff --git a/ko-osgi-test/src/test/java/org/netbeans/html/ko/osgi/test/KnockoutEquinoxIT.java b/ko-osgi-test/src/test/java/org/netbeans/html/ko/osgi/test/KnockoutEquinoxIT.java
new file mode 100644
index 0000000..6c57f82
--- /dev/null
+++ b/ko-osgi-test/src/test/java/org/netbeans/html/ko/osgi/test/KnockoutEquinoxIT.java
@@ -0,0 +1,245 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.ko.osgi.test;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.ServiceLoader;
+import java.util.concurrent.Callable;
+import java.util.jar.JarFile;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.netbeans.html.boot.spi.Fn;
+import org.netbeans.html.json.tck.KOTest;
+import org.netbeans.html.json.tck.KnockoutTCK;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.launch.Framework;
+import org.osgi.framework.launch.FrameworkFactory;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.fail;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.Factory;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class KnockoutEquinoxIT {
+    private static final Logger LOG = Logger.getLogger(KnockoutEquinoxIT.class.getName());
+    private static Framework framework;
+    private static File dir;
+    static Framework framework() throws Exception {
+        if (framework != null) {
+            return framework;
+        }
+        for (FrameworkFactory ff : ServiceLoader.load(FrameworkFactory.class)) {
+            
+            String basedir = System.getProperty("basedir");
+            assertNotNull("basedir preperty provided", basedir);
+            File target = new File(basedir, "target");
+            dir = new File(target, "osgi");
+            dir.mkdirs();
+            
+            Map<String,String> config = new HashMap<String, String>();
+            config.put(Constants.FRAMEWORK_STORAGE, dir.getPath());
+            config.put(Constants.FRAMEWORK_STORAGE_CLEAN, "true");
+            config.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, "sun.misc,"
+                + "javafx.application,"
+                + "javafx.beans,"
+                + "javafx.beans.property,"
+                + "javafx.beans.value,"
+                + "javafx.collections,"
+                + "javafx.concurrent,"
+                + "javafx.event,"
+                + "javafx.geometry,"
+                + "javafx.scene,"
+                + "javafx.scene.control,"
+                + "javafx.scene.image,"
+                + "javafx.scene.layout,"
+                + "javafx.scene.text,"
+                + "javafx.scene.web,"
+                + "javafx.stage,"
+                + "javafx.util,"
+                + "netscape.javascript"
+            );
+            config.put("osgi.hook.configurators.include", "org.netbeans.html.equinox.agentclass.AgentHook");
+            framework = ff.newFramework(config);
+            framework.init();
+            loadClassPathBundles(framework);
+            framework.start();
+            for (Bundle b : framework.getBundleContext().getBundles()) {
+                try {
+                    if (b.getSymbolicName().contains("equinox-agentclass-hook")) {
+                        continue;
+                    }
+                    if (b.getSymbolicName().contains("glassfish.grizzly")) {
+                        continue;
+                    }
+                    b.start();
+                    LOG.log(Level.INFO, "Started {0}", b.getSymbolicName());
+                } catch (BundleException ex) {
+                    throw new IllegalStateException("Cannot start bundle " + b.getSymbolicName(), ex);
+                }
+            }
+            return framework;
+        }
+        fail("No OSGi framework in the path");
+        return null;
+    }
+    
+    @AfterClass public static void cleanUp() throws Exception {
+        if (framework != null) framework.stop();
+        clearUpDir(dir);
+    }
+    private static void clearUpDir(File dir) {
+        if (dir.isDirectory()) {
+            for (File f : dir.listFiles()) {
+                clearUpDir(f);
+            }
+        }
+        dir.delete();
+    }
+    
+    
+
+    private static void loadClassPathBundles(Framework f) throws IOException, BundleException {
+        for (String jar : System.getProperty("java.class.path").split(File.pathSeparator)) {
+            File file = new File(jar);
+            if (!file.isFile()) {
+                LOG.info("Not loading " + file);
+                continue;
+            }
+            JarFile jf = new JarFile(file);
+            final String name = jf.getManifest().getMainAttributes().getValue("Bundle-SymbolicName");
+            jf.close();
+            if (name != null) {
+                if (name.contains("org.eclipse.osgi")) {
+                    continue;
+                }
+                if (name.contains("testng")) {
+                    continue;
+                }
+                final String path = "reference:" + file.toURI().toString();
+                try {
+                    Bundle b = f.getBundleContext().installBundle(path);
+                } catch (BundleException ex) {
+                    LOG.log(Level.WARNING, "Cannot install " + file, ex);
+                }
+            }
+        }
+    }
+    
+    private static Class<?> loadOSGiClass(Class<?> c) throws Exception {
+        return KnockoutEquinoxTCKImpl.loadOSGiClass(c.getName(), KnockoutEquinoxIT.framework().getBundleContext());
+    }
+    
+    private static Class<?> browserClass;
+    private static Object browserContext;
+    
+    @Factory public static Object[] compatibilityTests() throws Exception {
+        Class<?> tck = loadOSGiClass(KnockoutTCK.class);
+        Class<?> peer = loadOSGiClass(KnockoutEquinoxTCKImpl.class);
+        // initialize the TCK
+        Callable<Class[]> inst = (Callable<Class[]>) peer.newInstance();
+        
+        Class[] arr = inst.call();
+        for (int i = 0; i < arr.length; i++) {
+            if (arr[i].getClassLoader() == ClassLoader.getSystemClassLoader()) {
+                fail("Should be an OSGi class: " + arr[i]);
+            }
+        }
+        
+        URI uri = DynamicHTTP.initServer();
+
+        Method start = peer.getMethod("start", URI.class);
+        start.invoke(null, uri);
+        
+        ClassLoader l = getClassLoader();
+        List<Object> res = new ArrayList<Object>();
+        for (int i = 0; i < arr.length; i++) {
+            seekKOTests(arr[i], res);
+        }
+        return res.toArray();
+    }
+
+    private static void seekKOTests(Class<?> c, List<Object> res) throws SecurityException, ClassNotFoundException {
+        Class<? extends Annotation> koTest =
+            c.getClassLoader().loadClass(KOTest.class.getName()).
+            asSubclass(Annotation.class);
+        for (Method m : c.getMethods()) {
+            if (m.getAnnotation(koTest) != null) {
+                res.add(new KOFx(browserContext, m));
+            }
+        }
+    }
+
+    static synchronized ClassLoader getClassLoader() throws InterruptedException {
+        while (browserClass == null) {
+            KnockoutEquinoxIT.class.wait();
+        }
+        return browserClass.getClassLoader();
+    }
+    
+    public static synchronized void initialized(Class<?> browserCls, Object presenter) throws Exception {
+        browserClass = browserCls;
+        browserContext = presenter;
+        KnockoutEquinoxIT.class.notifyAll();
+    }
+
+    static Closeable activateInOSGi(Object presenter) throws Exception {
+        Class<?> presenterClass = loadOSGiClass(Fn.Presenter.class);
+        Class<?> fnClass = loadOSGiClass(Fn.class);
+        Method m = fnClass.getMethod("activate", presenterClass);
+        return (Closeable) m.invoke(null, presenter);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko-osgi-test/src/test/resources/org/netbeans/html/ko/osgi/test/test.html
----------------------------------------------------------------------
diff --git a/ko-osgi-test/src/test/resources/org/netbeans/html/ko/osgi/test/test.html b/ko-osgi-test/src/test/resources/org/netbeans/html/ko/osgi/test/test.html
new file mode 100644
index 0000000..226c9f5
--- /dev/null
+++ b/ko-osgi-test/src/test/resources/org/netbeans/html/ko/osgi/test/test.html
@@ -0,0 +1,56 @@
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>Knockout.fx Execution Harness</title>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <meta name="viewport" content="width=device-width">
+    </head>
+    <body>
+        <h1>Knockout.fx in Equinox Execution Harness</h1>
+    </body>
+    <script></script>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko-ws-tyrus/pom.xml
----------------------------------------------------------------------
diff --git a/ko-ws-tyrus/pom.xml b/ko-ws-tyrus/pom.xml
new file mode 100644
index 0000000..9415d83
--- /dev/null
+++ b/ko-ws-tyrus/pom.xml
@@ -0,0 +1,194 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2016 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<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">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.netbeans.html</groupId>
+    <artifactId>pom</artifactId>
+    <version>2.0-SNAPSHOT</version>
+  </parent>
+  <groupId>org.netbeans.html</groupId>
+  <artifactId>ko-ws-tyrus</artifactId>
+  <version>2.0-SNAPSHOT</version>
+  <packaging>bundle</packaging>
+  <name>Tyrus Based WebSockets</name>
+  <url>http://maven.apache.org</url>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.netbeans.html</groupId>
+                <artifactId>html4j-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <configuration>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <bundleSymbolicName>org.netbeans.html.ko-ws-tyrus</bundleSymbolicName>
+  </properties>
+  <dependencies>
+    <!-- compile only deps -->
+    <dependency>
+      <groupId>org.netbeans.api</groupId>
+      <artifactId>org-openide-util-lookup</artifactId>
+      <type>jar</type>
+      <scope>provided</scope>
+    </dependency>
+
+    <!-- compile + runtime -->      
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>net.java.html</artifactId>
+      <version>${project.version}</version>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>net.java.html.json</artifactId>
+      <version>${project.version}</version>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+        <groupId>de.twentyeleven.skysail</groupId>
+        <artifactId>org.json-osgi</artifactId>
+    </dependency>
+    <dependency>
+      <artifactId>javax.websocket-api</artifactId>
+      <groupId>javax.websocket</groupId>
+      <type>jar</type>
+      <version>1.0</version>
+    </dependency>
+
+    <!-- tyrus runtime -->    
+    <dependency>
+        <groupId>org.glassfish.tyrus</groupId>
+        <artifactId>tyrus-client</artifactId>
+        <version>1.3.1</version>
+        <scope>runtime</scope>
+    </dependency>
+    <dependency>
+        <groupId>org.glassfish.tyrus</groupId>
+        <artifactId>tyrus-container-grizzly-client</artifactId>
+        <version>1.3.1</version>
+        <scope>runtime</scope>
+    </dependency>
+    
+    
+    
+    <!-- test only deps -->
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>net.java.html.boot</artifactId>
+      <version>${project.version}</version>
+      <scope>provided</scope>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>net.java.html.json.tck</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>ko4j</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+      <groupId>org.glassfish.grizzly</groupId>
+      <artifactId>grizzly-http-server-core</artifactId>
+      <version>${grizzly.version}</version>
+      <scope>test</scope>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+      <groupId>org.glassfish.grizzly</groupId>
+      <artifactId>grizzly-websockets-server</artifactId>
+      <version>${grizzly.version}</version>
+      <scope>test</scope>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+        <groupId>${project.groupId}</groupId>
+        <artifactId>net.java.html.boot.fx</artifactId>
+        <version>${project.version}</version>
+        <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.glassfish.grizzly</groupId>
+      <artifactId>grizzly-http-server</artifactId>
+      <version>${grizzly.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+        <groupId>org.glassfish.grizzly</groupId>
+        <artifactId>grizzly-http-servlet</artifactId>
+        <version>${grizzly.version}</version>
+        <scope>test</scope>
+    </dependency>    
+    <dependency>
+        <groupId>javax.servlet</groupId>
+        <artifactId>javax.servlet-api</artifactId>
+        <scope>test</scope>
+        <version>3.1.0</version>
+    </dependency>
+  </dependencies>
+    <description>An implementation module that provides support for WebSocket protocol on JDK7. 
+No need to use it when running on JDK8 with FX WebView supporting WebSocket directly.</description>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/LoadJSON.java
----------------------------------------------------------------------
diff --git a/ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/LoadJSON.java b/ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/LoadJSON.java
new file mode 100644
index 0000000..c91f6aa
--- /dev/null
+++ b/ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/LoadJSON.java
@@ -0,0 +1,301 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.wstyrus;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PushbackInputStream;
+import java.io.Reader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.logging.Logger;
+import net.java.html.js.JavaScriptBody;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.json.JSONTokener;
+import org.netbeans.html.json.spi.JSONCall;
+
+/** This is an implementation package - just
+ * include its JAR on classpath and use official {@link Context} API
+ * to access the functionality.
+ *
+ * @author Jaroslav Tulach
+ */
+final class LoadJSON implements Runnable {
+    private static final Logger LOG = Logger.getLogger(LoadJSON.class.getName());
+    private static final Executor REQ = Executors.newCachedThreadPool(new ThreadFactory() {
+        @Override
+        public Thread newThread(Runnable runnable) {
+            Thread thread = Executors.defaultThreadFactory().newThread(runnable);
+            thread.setDaemon(true);
+            return thread;
+        }
+    });
+
+    private final JSONCall call;
+    private final URL base;
+
+
+    private LoadJSON(JSONCall call) {
+        this.call = call;
+        this.base = null;
+    }
+
+    public static void loadJSON(JSONCall call) {
+        assert !"WebSocket".equals(call.getMethod());
+        REQ.execute(new LoadJSON((call)));
+    }
+
+    @Override
+    public void run() {
+        final String url;
+        Throwable error = null;
+        Object json = null;
+
+        if (call.isJSONP()) {
+            url = call.composeURL("dummy");
+        } else {
+            url = call.composeURL(null);
+        }
+        try {
+            final URL u = new URL(base, url.replace(" ", "%20"));
+            URLConnection conn = u.openConnection();
+            if (call.isDoOutput()) {
+                conn.setDoOutput(true);
+            }
+            String h = call.getHeaders();
+            if (h != null) {
+                int pos = 0;
+                while (pos < h.length()) {
+                    int tagEnd = h.indexOf(':', pos);
+                    if (tagEnd == -1) {
+                        break;
+                    }
+                    int r = h.indexOf('\r', tagEnd);
+                    int n = h.indexOf('\n', tagEnd);
+                    if (r == -1) {
+                        r = h.length();
+                    }
+                    if (n == -1) {
+                        n = h.length();
+                    }
+                    String key = h.substring(pos, tagEnd).trim();
+                    String val = h.substring(tagEnd + 1, Math.min(r, n)).trim();
+                    conn.setRequestProperty(key, val);;
+                    pos = Math.max(r, n);
+                }
+            }
+            if (call.getMethod() != null && conn instanceof HttpURLConnection) {
+                ((HttpURLConnection) conn).setRequestMethod(call.getMethod());
+            }
+            if (call.isDoOutput()) {
+                final OutputStream os = conn.getOutputStream();
+                call.writeData(os);
+                os.flush();
+            }
+            final PushbackInputStream is = new PushbackInputStream(
+                conn.getInputStream(), 1
+            );
+            boolean[] arrayOrString = { false, false };
+            detectJSONType(call.isJSONP(), is, arrayOrString);
+            try {
+                if (arrayOrString[1]) {
+                    throw new JSONException("");
+                }
+                JSONTokener tok = createTokener(is);
+                Object obj;
+                obj = arrayOrString[0] ? new JSONArray(tok) : new JSONObject(tok);
+                json = convertToArray(obj);
+            } catch (JSONException ex) {
+                Reader r = new InputStreamReader(is, "UTF-8");
+                StringBuilder sb = new StringBuilder();
+                for (;;) {
+                    int ch = r.read();
+                    if (ch == -1) {
+                        break;
+                    }
+                    sb.append((char)ch);
+                }
+                json = sb.toString();
+            }
+        } catch (IOException ex) {
+            error = ex;
+        } finally {
+            if (error != null) {
+                call.notifyError(error);
+            } else {
+                call.notifySuccess(json);
+            }
+        }
+    }
+
+    private static void detectJSONType(boolean skipAnything, final PushbackInputStream is, boolean[] arrayOrString) throws IOException {
+        for (;;) {
+            int ch = is.read();
+            if (ch == -1) {
+                arrayOrString[1] = true;
+                break;
+            }
+            if (Character.isWhitespace(ch)) {
+                continue;
+            }
+
+            if (ch == '[') {
+                is.unread(ch);
+                arrayOrString[0] = true;
+                break;
+            }
+            if (ch == '{') {
+                is.unread(ch);
+                break;
+            }
+            if (!skipAnything) {
+                is.unread(ch);
+                arrayOrString[1] = true;
+                break;
+            }
+        }
+    }
+
+    private static JSONTokener createTokener(InputStream is) throws IOException {
+        Reader r = new InputStreamReader(is, "UTF-8");
+        try {
+            return new JSONTokener(r);
+        } catch (LinkageError ex) {
+            // phones may carry outdated version of JSONTokener
+            StringBuilder sb = new StringBuilder();
+            for (;;) {
+                int ch = r.read();
+                if (ch == -1) {
+                    break;
+                }
+                sb.append((char)ch);
+            }
+            return new JSONTokener(sb.toString());
+        }
+    }
+
+    static Object convertToArray(Object o) throws JSONException {
+        if (o instanceof JSONArray) {
+            JSONArray ja = (JSONArray)o;
+            Object[] arr = new Object[ja.length()];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = convertToArray(ja.get(i));
+            }
+            return arr;
+        } else if (o instanceof JSONObject) {
+            JSONObject obj = (JSONObject)o;
+            Iterator it = obj.keys();
+            List<Object> collect = new ArrayList<Object>();
+            while (it.hasNext()) {
+                String key = (String)it.next();
+                final Object val = obj.get(key);
+                final Object newVal = convertToArray(val);
+                if (val != newVal) {
+                    collect.add(key);
+                    collect.add(newVal);
+                }
+            }
+            int size = collect.size();
+            for (int i = 0; i < size; i += 2) {
+                obj.put((String) collect.get(i), collect.get(i + 1));
+            }
+            return obj;
+        } else if (o == JSONObject.NULL) {
+            return null;
+        } else {
+            return o;
+        }
+    }
+
+    public static void extractJSON(Object jsonObject, String[] props, Object[] values) {
+        if (jsonObject instanceof JSONObject) {
+            JSONObject obj = (JSONObject)jsonObject;
+            for (int i = 0; i < props.length; i++) {
+                Object val = obj.opt(props[i]);
+                if (val == JSONObject.NULL) {
+                    val = null;
+                }
+                values[i] = val;
+            }
+            return;
+        }
+        for (int i = 0; i < props.length; i++) {
+            values[i] = getProperty(jsonObject, props[i]);
+        }
+    }
+
+    @JavaScriptBody(args = {"object", "property"}, body =
+        "var ret;\n" + 
+        "if (property === null) ret = object;\n" + 
+        "else if (object === null) ret = null;\n" + 
+        "else ret = object[property];\n" + 
+        "return ret ? (typeof ko === 'undefined' ? ret : ko.utils.unwrapObservable(ret)) : null;"
+    )
+    private static Object getProperty(Object object, String property) {
+        return null;
+    }
+
+    public static Object parse(InputStream is) throws IOException {
+        try {
+            PushbackInputStream push = new PushbackInputStream(is, 1);
+            boolean[] arrayOrString = { false, false };
+            detectJSONType(false, push, arrayOrString);
+            JSONTokener t = createTokener(push);
+            Object obj = arrayOrString[0] ? new JSONArray(t) : new JSONObject(t);
+            return convertToArray(obj);
+        } catch (JSONException ex) {
+            throw new IOException(ex);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/TyrusContext.java
----------------------------------------------------------------------
diff --git a/ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/TyrusContext.java b/ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/TyrusContext.java
new file mode 100644
index 0000000..498da94
--- /dev/null
+++ b/ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/TyrusContext.java
@@ -0,0 +1,209 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.wstyrus;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Iterator;
+import javax.websocket.ClientEndpoint;
+import javax.websocket.ContainerProvider;
+import javax.websocket.OnClose;
+import javax.websocket.OnError;
+import javax.websocket.OnMessage;
+import javax.websocket.OnOpen;
+import javax.websocket.Session;
+import javax.websocket.WebSocketContainer;
+import net.java.html.json.OnReceive;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.json.spi.JSONCall;
+import org.netbeans.html.json.spi.Transfer;
+import org.netbeans.html.json.spi.WSTransfer;
+import org.netbeans.html.wstyrus.TyrusContext.Comm;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.json.JSONTokener;
+import org.openide.util.lookup.ServiceProvider;
+
+/** This is an implementation module that provides support for
+ * WebSocket protocol for {@link OnReceive} communication end point for
+ * JDK7.
+ * <p>
+ * Don't deal with this module directly, rather use the 
+ * {@link OnReceive @OnReceive(url="ws://...", ...)} API to establish your
+ * WebSocket connection.
+ * <p>
+ * There is no need to include this module in your application if you are
+ * running on JDK8. JDK8 WebView provides its own implementation of the
+ * WebSocket API based on WebSocket object inside a browser. This is included
+ * in the <code>org.netbeans.html:ko4j:1.0</code> module.
+ *
+ * @author Jaroslav Tulach
+ */
+@Contexts.Id("tyrus")
+@ServiceProvider(service = Contexts.Provider.class)
+public final class TyrusContext 
+implements Contexts.Provider, WSTransfer<Comm>, Transfer {
+    @Override
+    public void fillContext(Contexts.Builder context, Class<?> requestor) {
+        // default WebSocket transfer implementation is registered
+        // in ko-fx module with 100, provide this one as a fallback only
+        context.register(WSTransfer.class, this, 1000);
+        context.register(Transfer.class, this, 1000);
+    }
+
+    @Override
+    public Comm open(String url, JSONCall callback) {
+        try {
+            return new Comm(new URI(url), callback);
+        } catch (URISyntaxException ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+
+    @Override
+    public void send(Comm socket, JSONCall data) {
+        socket.session.getAsyncRemote().sendText(data.getMessage());
+    }
+
+    @Override
+    public void close(Comm socket) {
+        try {
+            final Session s = socket.session;
+            if (s != null) {
+                s.close();
+            }
+        } catch (IOException ex) {
+            socket.callback.notifyError(ex);
+        }
+    }
+
+    @Override
+    public void extract(Object obj, String[] props, Object[] values) {
+        LoadJSON.extractJSON(obj, props, values);
+    }
+
+    @Override
+    public Object toJSON(InputStream is) throws IOException {
+        return LoadJSON.parse(is);
+    }
+
+    @Override
+    public void loadJSON(JSONCall call) {
+        LoadJSON.loadJSON(call);
+    }
+    
+    /** Implementation class in an implementation. Represents a {@link ClientEndpoint} of the
+     * WebSocket channel. You are unlikely to get on hold of it.
+     */
+    @ClientEndpoint
+    public static final class Comm {
+        private final JSONCall callback;
+        private Session session;
+
+        Comm(final URI url, JSONCall callback) {
+            this.callback = callback;
+            try {
+                final WebSocketContainer c = ContainerProvider.getWebSocketContainer();
+                c.connectToServer(Comm.this, url);
+            } catch (Exception ex) {
+                wasAnError(ex);
+            }
+        }
+
+        @OnOpen
+        public synchronized void open(Session s) {
+            this.session = s;
+            callback.notifySuccess(null);
+        }
+
+        @OnClose
+        public void close() {
+            this.session = null;
+            callback.notifyError(null);
+        }
+
+        @OnMessage
+        public void message(final String orig, Session s) {
+            Object json;
+            String data = orig.trim();
+            try {
+                JSONTokener tok = new JSONTokener(data);
+                Object obj = data.startsWith("[") ? new JSONArray(tok) : new JSONObject(tok);
+                json = convertToArray(obj);
+            } catch (JSONException ex) {
+                json = data;
+            }
+            callback.notifySuccess(json);
+        }
+
+        @OnError
+        public void wasAnError(Throwable t) {
+            callback.notifyError(t);
+        }
+
+        static Object convertToArray(Object o) throws JSONException {
+            if (o instanceof JSONArray) {
+                JSONArray ja = (JSONArray) o;
+                Object[] arr = new Object[ja.length()];
+                for (int i = 0; i < arr.length; i++) {
+                    arr[i] = convertToArray(ja.get(i));
+                }
+                return arr;
+            } else if (o instanceof JSONObject) {
+                JSONObject obj = (JSONObject) o;
+                Iterator it = obj.keys();
+                while (it.hasNext()) {
+                    String key = (String) it.next();
+                    obj.put(key, convertToArray(obj.get(key)));
+                }
+                return obj;
+            } else {
+                return o;
+            }
+        }
+        
+    } // end of Comm
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusDynamicHTTP.java
----------------------------------------------------------------------
diff --git a/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusDynamicHTTP.java b/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusDynamicHTTP.java
new file mode 100644
index 0000000..96b41cd
--- /dev/null
+++ b/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusDynamicHTTP.java
@@ -0,0 +1,262 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.wstyrus;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.glassfish.grizzly.PortRange;
+import org.glassfish.grizzly.http.server.HttpHandler;
+import org.glassfish.grizzly.http.server.HttpServer;
+import org.glassfish.grizzly.http.server.NetworkListener;
+import org.glassfish.grizzly.http.server.Request;
+import org.glassfish.grizzly.http.server.Response;
+import org.glassfish.grizzly.http.server.ServerConfiguration;
+import org.glassfish.grizzly.websockets.WebSocket;
+import org.glassfish.grizzly.websockets.WebSocketAddOn;
+import org.glassfish.grizzly.websockets.WebSocketApplication;
+import org.glassfish.grizzly.websockets.WebSocketEngine;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+final class TyrusDynamicHTTP extends HttpHandler {
+    private static int resourcesCount;
+    private static List<Resource> resources;
+    private static ServerConfiguration conf;
+    private static HttpServer server;
+
+    private TyrusDynamicHTTP() {
+    }
+
+    static URI initServer() throws Exception {
+        server = HttpServer.createSimpleServer(null, new PortRange(8080, 65535));
+        final WebSocketAddOn addon = new WebSocketAddOn();
+        for (NetworkListener listener : server.getListeners()) {
+            listener.registerAddOn(addon);
+        }
+        resources = new ArrayList<Resource>();
+
+        conf = server.getServerConfiguration();
+        final TyrusDynamicHTTP dh = new TyrusDynamicHTTP();
+
+        conf.addHttpHandler(dh, "/");
+
+        server.start();
+
+        return pageURL("http", server, "/test.html");
+    }
+
+    @Override
+    public void service(Request request, Response response) throws Exception {
+        if ("/test.html".equals(request.getRequestURI())) {
+            response.setContentType("text/html");
+            final InputStream is = TyrusDynamicHTTP.class.getResourceAsStream("test.html");
+            copyStream(is, response.getOutputStream(), null);
+            return;
+        }
+        if ("/dynamic".equals(request.getRequestURI())) {
+            String mimeType = request.getParameter("mimeType");
+            List<String> params = new ArrayList<String>();
+            boolean webSocket = false;
+            for (int i = 0;; i++) {
+                String p = request.getParameter("param" + i);
+                if (p == null) {
+                    break;
+                }
+                if ("protocol:ws".equals(p)) {
+                    webSocket = true;
+                    continue;
+                }
+                params.add(p);
+            }
+            final String cnt = request.getParameter("content");
+            String mangle = cnt.replace("%20", " ").replace("%0A", "\n");
+            ByteArrayInputStream is = new ByteArrayInputStream(mangle.getBytes("UTF-8"));
+            URI url;
+            final Resource res = new Resource(is, mimeType, "/dynamic/res" + ++resourcesCount, params.toArray(new String[params.size()]));
+            if (webSocket) {
+                url = registerWebSocket(res);
+            } else {
+                url = registerResource(res);
+            }
+            response.getWriter().write(url.toString());
+            response.getWriter().write("\n");
+            return;
+        }
+
+        for (Resource r : resources) {
+            if (r.httpPath.equals(request.getRequestURI())) {
+                response.setContentType(r.httpType);
+                r.httpContent.reset();
+                String[] params = null;
+                if (r.parameters.length != 0) {
+                    params = new String[r.parameters.length];
+                    for (int i = 0; i < r.parameters.length; i++) {
+                        params[i] = request.getParameter(r.parameters[i]);
+                        if (params[i] == null) {
+                            if ("http.method".equals(r.parameters[i])) {
+                                params[i] = request.getMethod().toString();
+                            } else if ("http.requestBody".equals(r.parameters[i])) {
+                                Reader rdr = request.getReader();
+                                StringBuilder sb = new StringBuilder();
+                                for (;;) {
+                                    int ch = rdr.read();
+                                    if (ch == -1) {
+                                        break;
+                                    }
+                                    sb.append((char) ch);
+                                }
+                                params[i] = sb.toString();
+                            } else if (r.parameters[i].startsWith("http.header.")) {
+                                params[i] = request.getHeader(r.parameters[i].substring(12));
+                            }
+                        }
+                        if (params[i] == null) {
+                            params[i] = "null";
+                        }
+                    }
+                }
+
+                copyStream(r.httpContent, response.getOutputStream(), null, params);
+            }
+        }
+    }
+
+    private URI registerWebSocket(Resource r) {
+        WebSocketEngine.getEngine().register("", r.httpPath, new WS(r));
+        return pageURL("ws", server, r.httpPath);
+    }
+
+    private URI registerResource(Resource r) {
+        if (!resources.contains(r)) {
+            resources.add(r);
+            conf.addHttpHandler(this, r.httpPath);
+        }
+        return pageURL("http", server, r.httpPath);
+    }
+
+    private static URI pageURL(String proto, HttpServer server, final String page) {
+        NetworkListener listener = server.getListeners().iterator().next();
+        int port = listener.getPort();
+        try {
+            return new URI(proto + "://localhost:" + port + page);
+        } catch (URISyntaxException ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+
+    static final class Resource {
+
+        final InputStream httpContent;
+        final String httpType;
+        final String httpPath;
+        final String[] parameters;
+
+        Resource(InputStream httpContent, String httpType, String httpPath,
+            String[] parameters) {
+            httpContent.mark(Integer.MAX_VALUE);
+            this.httpContent = httpContent;
+            this.httpType = httpType;
+            this.httpPath = httpPath;
+            this.parameters = parameters;
+        }
+    }
+
+    static void copyStream(InputStream is, OutputStream os, String baseURL, String... params) throws IOException {
+        for (;;) {
+            int ch = is.read();
+            if (ch == -1) {
+                break;
+            }
+            if (ch == '$' && params.length > 0) {
+                int cnt = is.read() - '0';
+                if (baseURL != null && cnt == 'U' - '0') {
+                    os.write(baseURL.getBytes("UTF-8"));
+                } else {
+                    if (cnt >= 0 && cnt < params.length) {
+                        os.write(params[cnt].getBytes("UTF-8"));
+                    } else {
+                        os.write('$');
+                        os.write(cnt + '0');
+                    }
+                }
+            } else {
+                os.write(ch);
+            }
+        }
+    }
+
+    private static class WS extends WebSocketApplication {
+        private final Resource r;
+
+        private WS(Resource r) {
+            this.r = r;
+        }
+
+        @Override
+        public void onMessage(WebSocket socket, String text) {
+            try {
+                r.httpContent.reset();
+                ByteArrayOutputStream out = new ByteArrayOutputStream();
+                copyStream(r.httpContent, out, null, text);
+                String s = new String(out.toByteArray(), "UTF-8");
+                socket.send(s);
+            } catch (IOException ex) {
+                LOG.log(Level.WARNING, null, ex);
+            }
+        }
+        private static final Logger LOG = Logger.getLogger(WS.class.getName());
+
+    }
+}


[11/24] incubator-netbeans-html4j git commit: [INFRA-15006] Initial donation of HTML/Java NetBeans API. Equivalent to the content of html4j-donation-review.zip donated as part of ApacheNetBeansDonation1.zip with SHA256 being 7f2ca0f61953a190613c9a0fbcc1b

Posted by jt...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/main/java/net/java/html/json/OnReceive.java
----------------------------------------------------------------------
diff --git a/json/src/main/java/net/java/html/json/OnReceive.java b/json/src/main/java/net/java/html/json/OnReceive.java
new file mode 100644
index 0000000..e6fc741
--- /dev/null
+++ b/json/src/main/java/net/java/html/json/OnReceive.java
@@ -0,0 +1,204 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** Static methods in classes annotated by {@link Model}
+ * can be marked by this annotation to establish a
+ * <a href="http://en.wikipedia.org/wiki/JSON">JSON</a>
+ * communication point. The first argument should be the
+ * associated {@link Model} class. The second argument can
+ * be another class generated by {@link Model} annotation,
+ * or array of such classes or (since 0.8.1 version) a
+ * {@link java.util.List} of such classes.
+ * The associated model class then gets new method to invoke a network
+ * connection asynchronously. Example follows:
+ *
+ * <pre>
+ * {@link Model @Model}(className="MyModel", properties={
+ *   {@link Property @Property}(name = "people", type=Person.class, array=true)
+ * })
+ * class MyModelImpl {
+ *   {@link Model @Model}(className="Person", properties={
+ *     {@link Property @Property}(name = "firstName", type=String.class),
+ *     {@link Property @Property}(name = "lastName", type=String.class)
+ *   })
+ *   static class PersonImpl {
+ *     {@link ComputedProperty @ComputedProperty}
+ *     static String fullName(String firstName, String lastName) {
+ *       return firstName + " " + lastName;
+ *     }
+ *   }
+ *
+ *   {@link OnReceive @OnReceive}(url = "{protocol}://your.server.com/person/{name}")
+ *   static void getANewPerson(MyModel m, Person p) {
+ *     System.out.println("Adding " + p.getFullName() + '!');
+ *     m.getPeople().add(p);
+ *   }
+ *
+ *   // the above will generate method <code>getANewPerson</code> in class <code>MyModel</code>.
+ *   // with <code>protocol</code> and <code>name</code> arguments
+ *   // which asynchronously contacts the server and in case of success calls
+ *   // your {@link OnReceive @OnReceive} with parsed in data
+ *
+ *   {@link Function @Function}
+ *   static void requestSmith(MyModel m) {
+ *     m.getANewPerson("http", "Smith");
+ *   }
+ * }
+ * </pre>
+ * When the server returns <code>{ "firstName" : "John", "lastName" : "Smith" }</code>
+ * the system will print a message <em>Adding John Smith!</em>. It is not
+ * necessary to fully describe the server message - enumerate only the fields
+ * in the response you are interested in. The others will be discarded. So,
+ * if the server <code>{ "firstName" : "John", "lastName" : "Smith", "age" : 33 }</code>
+ * the above code will behave the same (e.g. ignore the <code>age</code>
+ * value).
+ * <p>
+ * One can use this method to communicate with the server
+ * via <a href="doc-files/websockets.html">WebSocket</a> protocol since version 0.6.
+ * Read the <a href="doc-files/websockets.html">tutorial</a> to see how.
+ * The method shall be non-private
+ * and unless {@link Model#instance() instance mode} is on also static.
+ * <p>
+ * Visit an <a target="_blank" href="http://dew.apidesign.org/dew/#7138581">on-line demo</a>
+ * to see REST access via {@link OnReceive} annotation.
+ *
+ * @author Jaroslav Tulach
+ * @since 0.3
+ */
+@Retention(RetentionPolicy.SOURCE)
+@Target(ElementType.METHOD)
+public @interface OnReceive {
+    /** The URL to connect to. Can contain variable names surrounded by '{' and '}'.
+     * Those names will then become parameters of the associated method.
+     *
+     * @return the (possibly parametrized) url to connect to
+     */
+    String url();
+
+    /** Specifies HTTP request headers. Array of header lines
+     * can contain variable names surrounded by '{' and '}'.
+     * Those names will then become parameters of the associated method
+     * (in addition to those added by {@link #url()} specification)
+     * and can only be used with plain JSON(P) requests.
+     * Headers are currently <b>not</b> supported by the
+     * <a href="doc-files/websockets.html">WebSockets protocol</a>.
+     * A sample follows. If you want to transmit <b>X-Birthday</b> header,
+     * you can do it like this:
+     * <pre>
+     * {@code @}{@link OnReceive}(url="http://your.server.org", headers = {
+     *   "X-Birthday: {dayOfBirth}"
+     * })
+     * <b>static void</b> knowingTheBirth({@link Model YourModel} model) {
+     *   // handle the reply
+     * }</pre>
+     * a method <b>knowingTheBirth</b> is generated in
+     * <code>YourModel</code> class with the <code>dayOfBirth</code> argument
+     * which can be called like this:
+     * <pre>
+     * yourModel.knowingTheBirth("10. 12. 1973");
+     * </pre>
+     *
+     * @return array of header lines - each line should be plain text with
+     *   a header name, followed by ":" and value usually specified as
+     *   '{' and '}' surrounded variable. The line shouldn't contain
+     *   newline or other control characters
+     * @since 1.2
+     */
+    String[] headers() default {};
+
+    /** Support for <a href="http://en.wikipedia.org/wiki/JSONP">JSONP</a> requires
+     * a callback from the server generated page to a function defined in the
+     * system. The name of such function is usually specified as a property
+     * (of possibly different names). By defining the <code>jsonp</code> attribute
+     * one turns on the <a href="http://en.wikipedia.org/wiki/JSONP">JSONP</a>
+     * transmission and specifies the name of the property. The property should
+     * also be used in the {@link #url()} attribute on appropriate place.
+     *
+     * @return name of a property to carry the name of <a href="http://en.wikipedia.org/wiki/JSONP">JSONP</a>
+     *    callback function.
+     */
+    String jsonp() default "";
+
+    /** The model class to be send to the server as JSON data.
+     * By default no data are sent. However certain {@link #method() transport methods}
+     * (like <code>"PUT"</code> and <code>"POST"</code>) require the
+     * data to be specified.
+     *
+     * @return name of a class generated using {@link Model @Model} annotation
+     * @since 0.3
+     */
+    Class<?> data() default Object.class;
+
+    /** The HTTP transfer method to use. Defaults to <code>"GET"</code>.
+     * Other typical methods include <code>"HEAD"</code>,
+     * <code>"DELETE"</code>, <code>"POST"</code>, <code>"PUT"</code>.
+     * The last two mentioned methods require {@link #data()} to be specified.
+     * <p>
+     * When {@link #jsonp() JSONP} transport is requested, the method
+     * has to be <code>"GET"</code>.
+     * <p>
+     * Since version 0.5 one can specify "<a href="doc-files/websockets.html">WebSocket</a>"
+     * as the communication method.
+     *
+     * @return name of the HTTP transfer method
+     * @since 0.3
+     */
+    String method() default "GET";
+
+    /** Name of a method in this class which should be called in case of
+     * an error. The method has to be non-private and take one model and
+     * one {@link Exception}
+     * parameter. If this method is not specified, the exception is just
+     * printed to console.
+     *
+     * @return name of method in this class
+     * @since 0.5
+     */
+    public String onError() default "";
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/main/java/net/java/html/json/Property.java
----------------------------------------------------------------------
diff --git a/json/src/main/java/net/java/html/json/Property.java b/json/src/main/java/net/java/html/json/Property.java
new file mode 100644
index 0000000..dfb1a8f
--- /dev/null
+++ b/json/src/main/java/net/java/html/json/Property.java
@@ -0,0 +1,98 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.List;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.json.spi.Technology;
+
+/** Represents a property in a class defined with {@link Model} annotation.
+ *
+ * @author Jaroslav Tulach
+ */
+@Retention(RetentionPolicy.SOURCE)
+@Target({})
+public @interface Property {
+    /** Name of the property. Will be used to define proper getter and setter
+     * in the associated class.
+     * 
+     * @return valid java identifier
+     */
+    String name();
+    
+    /** Type of the property. Can either be primitive type (like <code>int.class</code>,
+     * <code>double.class</code>, etc.), {@link String}, {@link Enum enum} or complex model
+     * class (defined by {@link Model} property).
+     * 
+     * @return the class of the property
+     */
+    Class<?> type();
+    
+    /** Is this property an array of the {@link #type()} or a single value?
+     * If the property is an array, only its getter (returning mutable {@link List} of
+     * the boxed {@link #type()}) is generated.
+     * 
+     * @return true, if this property is supposed to represent an array of values
+     */
+    boolean array() default false;
+
+    /** Can the value of the property be mutated without restriction or not.
+     * If a property is defined as <em>not mutable</em>, it defines
+     * semi-immutable value that can only be changed in construction time
+     * before the object is passed to underlying {@link Technology}. 
+     * Attempts to modify the object later yield {@link IllegalStateException}.
+     *
+     * Technologies may decide to represent such non-mutable
+     * property in more effective way - for
+     * example Knockout Java Bindings technology (with {@link Contexts.Id id} "ko4j")
+     * uses plain JavaScript value (number, string, array, boolean) rather
+     * than classical observable.
+     *
+     * @return false if the value cannot change after its <em>first use</em>
+     * @since 1.3
+     */
+    boolean mutable() default true;
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/main/java/net/java/html/json/doc-files/DukeHTML.png
----------------------------------------------------------------------
diff --git a/json/src/main/java/net/java/html/json/doc-files/DukeHTML.png b/json/src/main/java/net/java/html/json/doc-files/DukeHTML.png
new file mode 100644
index 0000000..8d7dc3e
Binary files /dev/null and b/json/src/main/java/net/java/html/json/doc-files/DukeHTML.png differ

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/main/java/net/java/html/json/doc-files/html4j.png
----------------------------------------------------------------------
diff --git a/json/src/main/java/net/java/html/json/doc-files/html4j.png b/json/src/main/java/net/java/html/json/doc-files/html4j.png
new file mode 100644
index 0000000..e281147
Binary files /dev/null and b/json/src/main/java/net/java/html/json/doc-files/html4j.png differ

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/main/java/net/java/html/json/doc-files/websockets.html
----------------------------------------------------------------------
diff --git a/json/src/main/java/net/java/html/json/doc-files/websockets.html b/json/src/main/java/net/java/html/json/doc-files/websockets.html
new file mode 100644
index 0000000..6cc93f2
--- /dev/null
+++ b/json/src/main/java/net/java/html/json/doc-files/websockets.html
@@ -0,0 +1,185 @@
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>WebSockets via @OnReceive</title>
+        <meta charset="UTF-8">
+        <link rel="stylesheet" type="text/css" href="../../../../../stylesheet.css" title="Style">
+    </head>
+    <body>
+        <h1>Using <a href="../OnReceive.html">@OnReceive</a> to Communicate 
+            via WebSockets Protocol</h1>
+        
+        There is a simple, yet flexible way to communicate with a server
+        via <a href="http://www.w3.org/TR/websockets/">WebSockets protocol</a>.
+        The support can transfer any classes generated by
+        <a href="../Model.html">@Model</a> annotation and reuses already 
+        existing <a href="../OnReceive.html">@OnReceive</a> infrastructure -
+        just defines detailed special behavior, which is described here.
+        
+        <h3>Define JSON Class</h3>
+        
+        The first step in using <em>WebSockets</em> is to create a model classes
+        to encapsulate communiation with the server. For example one for
+        sending requests and one for receiving replies:
+    <pre>
+    <a href="../Model.html">@Model</a>(className = "Comm", properties={})
+    <b>final class</b> Communication {
+        <a href="../Model.html">@Model</a>(className = "Request", properties = {
+            <a href="../Property.html">@Property</a>(name = "msg", type = MsgType<b>.class</b>),
+            <a href="../Property.html">@Property</a>(name = "username", type = String<b>.class</b>),
+            <a href="../Property.html">@Property</a>(name = "password", type = String<b>.class</b>),
+            <a href="../Property.html">@Property</a>(name = "gameId", type = String<b>.class</b>),
+            <a href="../Property.html">@Property</a>(name = "color", type = Color<b>.class</b>),
+            <a href="../Property.html">@Property</a>(name = "from", type = String<b>.class</b>),
+            <a href="../Property.html">@Property</a>(name = "to", type = String<b>.class</b>),
+            <a href="../Property.html">@Property</a>(name = "observer", type = boolean<b>.class</b>),
+        })
+        <b>static class</b> <em>RequestModel</em> {
+        }
+
+        <a href="../Model.html">@Model</a>(className = "Response", properties = {
+            <a href="../Property.html">@Property</a>(name = "msg", type = MsgType<b>.class</b>),
+            <a href="../Property.html">@Property</a>(name = "turn", type = Color<b>.class</b>),
+            <a href="../Property.html">@Property</a>(name = "color", type = Color<b>.class</b>),
+            <a href="../Property.html">@Property</a>(name = "gameId", type = String<b>.class</b>),
+            <a href="../Property.html">@Property</a>(name = "status", type = String<b>.class</b>),
+            <a href="../Property.html">@Property</a>(name = "moves", type = String<b>.class</b>, array = <b>true</b>),
+            <a href="../Property.html">@Property</a>(name = "from", type = String<b>.class</b>),
+            <a href="../Property.html">@Property</a>(name = "to", type = String<b>.class</b>),
+        })
+        <b>static class</b> <em>ResponseModel</em> {
+        }
+
+        <b>enum</b> <em>MsgType</em> {
+            CreateGame, QueryGames, SendMove, JoinGame, UpdateGame;
+        }
+        <b>enum</b> <em>Color</em> {
+            W, B;
+        }
+    }            
+    </pre>
+And then it is just a matter of creating the communication end point. As
+usual with <a href="../OnReceive.html">@OnReceive</a> annotation one starts
+with defining the response handler:
+    <pre>
+    <a href="../OnReceive.html">@OnReceive</a>(
+        data = <em>Request</em>.<b>class</b>, 
+        url = "{url}", 
+        method = <em>"WebSocket"</em>, 
+        onError = "anErrorHappened"
+    ) 
+    <b>static void</b> queryServer(Comm c, Response r) {
+        <b>if</b> (r == null) {
+            // <em>connection stablished!</em>
+            <b>return</b>;
+        }
+        // <em>message arrived!</em>
+        <b>switch</b> (r.getMsg()) {
+            <b>case</b> CreateGame: /* do something */ <b>break</b>;
+            <b>case</b> QueryGames: /* do something */ <b>break</b>;
+            <b>case</b> SendMove: /* do something */ <b>break</b>;
+            <b>case</b> JoinGame: /* do something */ <b>break</b>;
+            <b>case</b> UpdateGame: /* do something */ <b>break</b>;
+        }
+    }
+    <b>static void</b> anErrorHappened(Comm c, Exception t) {
+        if (t == null) {
+            // <em>OK, connection has been closed</em>
+        } else {
+            // <em>handle the error t somehow</em>
+        }
+    }
+    </pre>
+    The <a href="http://www.w3.org/TR/websockets/">WebSockets</a> specification
+    usually defines what should happen <em>onopen, onmessage, onclose and onerror</em>.
+    All these messages are supported in the previous example as well:
+    <ul>
+        <li><b>onopen</b> - the <em>queryServer</em> method is called with 
+            <code>null</code> message
+        </li>
+        <li><b>onmessage</b> - the <em>queryServer</em> method is called with 
+            non-null message
+        </li>
+        <li><b>onclose</b> - the <em>anErrorHappened</em> method is called with
+            <code>null</code> exception
+        </li>
+        <li><b>onerror</b> - the <em>anErrorHappened</em> method is called with
+            non-null exception
+        </li>
+    </ul>
+    Using the <a href="../OnReceive.html">@OnReceive</a> annotation instructs
+    its associated annotation processor to add appropriate send method
+    into the generated <code>Comm</code> class. One can use it to establish communication,
+    send messages and close the communication channel. Here are three methods
+    showing how to do it:
+    <pre>
+    <b>static void </b>connect(Comm c) {
+      // open a websocket channel:
+      c.queryServer("ws://server/path", <b>null</b>);
+      // the method returns immediately and starts establishing the connection
+      // once that is finished either <b>onopen</b> or <b>onerror</b> type of
+      // message is delivered
+    }
+
+    <b>static void </b>sendMessage(Comm c, Request r) {
+      // sends the message to already openned websocket channel:
+      c.queryServer("ws://server/path", r);
+    }
+
+    <b>static void </b>disconnect(Comm c) {
+      // sending <code>null</code> again, closes the connection
+      c.queryServer("ws://server/path", <b>null</b>);
+    }
+    </pre>
+    One cannot change the URL while a connection is open otherwise one
+    risks <code>IllegalStateException</code> runtime exceptions. To connect
+    to different web socket server, one needs to close the connection first and
+    only later open new one with different URL.
+    <p>
+    Enjoy <em>WebSocket</em>s via <a href="../OnReceive.html">@OnReceive</a>!
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/main/java/net/java/html/json/package.html
----------------------------------------------------------------------
diff --git a/json/src/main/java/net/java/html/json/package.html b/json/src/main/java/net/java/html/json/package.html
new file mode 100644
index 0000000..9384b47
--- /dev/null
+++ b/json/src/main/java/net/java/html/json/package.html
@@ -0,0 +1,118 @@
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<body>
+    
+    <p>
+    This API allows you to write your application logic in Java and 
+    present it using modern HTML rendering technologies like 
+    <a href="http://knockoutjs.com">Knockout</a>
+    and communicate with
+    a server via <a href="OnReceive.html">REST</a> or 
+    <a href="doc-files/websockets.html">WebSocket</a>s.
+    </p>
+    <img src="doc-files/html4j.png">
+    <p>
+    Use <a href="Model.html">@Model</a> annotation to define one or more
+    model classes with <a href="Property.html">properties</a>. Don't waste
+    time writing setters or getters - they will be generated for you. 
+    Just instantiate your classes and use them!
+    </p>
+    <p>
+    The class generator does not stop with getters and setters -- internally
+    it generates bindings for various HTML technologies. Just include appropriate
+    technology implementation on classpath of your application and your model
+    class(es) will automatically be bound to your HTML elements (after calling
+    <code>applyBindings()</code> on your model).
+    </p>
+    <p>
+    You don't have to bother with JavaScript. All your application logic is in
+    Java. The necessary JavaScript needed for the HTML bindings remains hidden
+    as an implementation detail of communication between the generated model
+    class(es) and appropriate technology bridge. For example
+    the <code>ko4j</code> module:
+    </p>
+    <pre>
+&lt;dependency&gt;
+  &lt;groupId&gt;org.netbeans.html&lt;/groupId&gt;
+  &lt;artifactId&gt;ko4j&lt;/artifactId&gt;
+  &lt;scope&gt;runtime&lt;/scope&gt;
+&lt;/dependency&gt;
+    </pre>
+    <p>
+    In case you decide to use <a target="ko" href="http://knockoutjs.com">Knockout</a>
+    as your rendering technology, it is recommended to look at its 
+    <a target="ko" href="http://knockoutjs.com/documentation/introduction.html">documentation</a>
+    (especially its HTML part) as <code>data-bind</code> syntax is exactly
+    what one has to use in the HTML file(s). Of course, one does not need
+    to bother with the JavaScript part, that is completely hidden in the
+    code generated when processing the {@link net.java.html.json.Model}
+    annotation.
+    </p>
+    
+    <p>
+    The model classes can be used for JSON based server communication. Just
+    use <a href="OnReceive.html">@OnReceive</a> annotation to define a communication
+    point in the model class. Please note, that the model classes can easily
+    be used on server as well - the same code can run
+    in your client as well as on your server. Just add following to your <em>pom.xml</em>
+    to use your classes generated by <a href="Model.html">@Model</a> annotation
+    as <a href="http://jersey.java.net" target="new">Jersey</a> entities:
+    </p>
+    <pre>
+&lt;dependency&gt;
+  &lt;groupId&gt;org.glassfish.jersey.media&lt;/groupId&gt;
+  &lt;artifactId&gt;html-json&lt;/artifactId&gt;
+  &lt;version&gt;2.6&lt;/version&gt;
+  &lt;scope&gt;runtime&lt;/scope&gt;
+&lt;/dependency&gt;
+    </pre>
+    <p>
+    Behavior of model classes can be enriched by using 
+    <a href="ComputedProperty.html">@ComputedProperty</a> annotation (to
+    define <em>derived</em> properties) and by 
+    <a href="Function.html">@Function</a> annotation to define handlers
+    to be invoked from the HTML elements.
+    </p>
+</body>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/main/java/org/netbeans/html/json/impl/Bindings.java
----------------------------------------------------------------------
diff --git a/json/src/main/java/org/netbeans/html/json/impl/Bindings.java b/json/src/main/java/org/netbeans/html/json/impl/Bindings.java
new file mode 100644
index 0000000..a8b0cc4
--- /dev/null
+++ b/json/src/main/java/org/netbeans/html/json/impl/Bindings.java
@@ -0,0 +1,133 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.json.impl;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import net.java.html.BrwsrCtx;
+import org.netbeans.html.json.spi.FunctionBinding;
+import org.netbeans.html.json.spi.PropertyBinding;
+import org.netbeans.html.json.spi.Proto;
+import org.netbeans.html.json.spi.Technology;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public final class Bindings<Data> {
+    private static final Logger LOG = Logger.getLogger(Bindings.class.getName()); 
+    
+    private Data data;
+    private final Technology<Data> bp;
+
+    private Bindings(Technology<Data> bp) {
+        this.bp = bp;
+    }
+    
+    public <M> PropertyBinding registerProperty(String propName, int index, M model, Proto.Type<M> access, byte propertyType) {
+        return PropertyBindingAccessor.create(access, this, propName, index, model, propertyType);
+    }
+
+    public static Bindings<?> apply(BrwsrCtx c) {
+        Technology<?> bp = JSON.findTechnology(c);
+        return apply(bp);
+    }
+    
+    private static <Data> Bindings<Data> apply(Technology<Data> bp) {
+        return new Bindings<Data>(bp);
+    }
+    
+    public final void finish(Object model, Object copyFrom, PropertyBinding[] propArr, FunctionBinding[] funcArr) {
+        assert data == null;
+        if (bp instanceof Technology.BatchCopy) {
+            Technology.BatchCopy<Data> bi = (Technology.BatchCopy<Data>)bp;
+            data = bi.wrapModel(model, copyFrom, propArr, funcArr);
+        } else if (bp instanceof Technology.BatchInit) {
+            Technology.BatchInit<Data> bi = (Technology.BatchInit<Data>)bp;
+            data = bi.wrapModel(model, propArr, funcArr);
+        } else {
+            data = bp.wrapModel(model);
+            for (PropertyBinding b : propArr) {
+                bp.bind(b, model, data);
+            }
+            for (FunctionBinding b : funcArr) {
+                bp.expose(b, model, data);
+            }
+        }
+    }
+    
+    
+    public Data koData() {
+        return data;
+    }
+
+    public void valueHasMutated(String firstName, Object oldValue, Object newValue) {
+        if (bp instanceof Technology.ValueMutated) {
+            Technology.ValueMutated<Data> vm = (Technology.ValueMutated<Data>)bp;
+            Object ov = JSON.find(oldValue, this);
+            Object nv = JSON.find(newValue, this);
+            vm.valueHasMutated(data, firstName, ov, nv);
+        } else {
+            bp.valueHasMutated(data, firstName);
+        }
+    }
+    
+    public void applyBindings(String id) {
+        if (bp instanceof Technology.ApplyId) {
+            Technology.ApplyId<Data> ai = (Technology.ApplyId<Data>) bp;
+            ai.applyBindings(id, data);
+            return;
+        }
+        if (id != null) {
+            LOG.log(Level.WARNING, 
+                "Technology {0} does not implement ApplyId extension. Can't apply to {1}. Applying globally.", 
+                new Object[]{bp, id}
+            );
+        }
+        bp.applyBindings(data);
+    }
+
+    Object wrapArray(Object[] arr) {
+        return bp.wrapArray(arr);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/main/java/org/netbeans/html/json/impl/JSON.java
----------------------------------------------------------------------
diff --git a/json/src/main/java/org/netbeans/html/json/impl/JSON.java b/json/src/main/java/org/netbeans/html/json/impl/JSON.java
new file mode 100644
index 0000000..287979c
--- /dev/null
+++ b/json/src/main/java/org/netbeans/html/json/impl/JSON.java
@@ -0,0 +1,533 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.json.impl;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import net.java.html.BrwsrCtx;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.json.spi.FunctionBinding;
+import org.netbeans.html.json.spi.JSONCall;
+import org.netbeans.html.json.spi.PropertyBinding;
+import org.netbeans.html.json.spi.Proto;
+import org.netbeans.html.json.spi.Technology;
+import org.netbeans.html.json.spi.Transfer;
+import org.netbeans.html.json.spi.WSTransfer;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public final class JSON {
+    private JSON() {
+    }
+
+    static Technology<?> findTechnology(BrwsrCtx c) {
+        Technology<?> t = Contexts.find(c, Technology.class);
+        return t == null ? EmptyTech.EMPTY : t;
+    }
+
+    public static Transfer findTransfer(BrwsrCtx c) {
+        Transfer t = Contexts.find(c, Transfer.class);
+        return t == null ? EmptyTech.EMPTY : t;
+    }
+
+    public static WSTransfer<?> findWSTransfer(BrwsrCtx c) {
+        WSTransfer<?> t = Contexts.find(c, WSTransfer.class);
+        return t == null ? EmptyTech.EMPTY : t;
+    }
+
+    public static <M> void readBindings(BrwsrCtx c, M model, Object value) {
+        Technology<?> tech = findTechnology(c);
+        if (tech instanceof Technology.BatchCopy) {
+            Proto p = findProto(model);
+            PropertyBindingAccessor.getBindings(p, true, value);
+        }
+    }
+    public static void extract(BrwsrCtx c, Object value, String[] props, Object[] values) {
+        Transfer t = findTransfer(c);
+        t.extract(value, props, values);
+    }
+
+    private static Object getProperty(BrwsrCtx c, Object obj, String prop) {
+        if (prop == null) return obj;
+
+        String[] arr = { prop };
+        Object[] val = { null };
+        extract(c, obj, arr, val);
+        return val[0];
+    }
+
+    public static String toJSON(Object value) {
+        if (value == null) {
+            return "null";
+        }
+        if (value instanceof Enum) {
+            value = value.toString();
+        }
+        if (value instanceof Character) {
+            value = Character.toString((Character)value);
+        }
+        if (value instanceof String) {
+            String s = (String)value;
+            int len = s.length();
+            StringBuilder sb = new StringBuilder(len + 10);
+            sb.append('"');
+            for (int i = 0; i < len; i++) {
+                char ch = s.charAt(i);
+                switch (ch) {
+                    case '\"': sb.append("\\\""); break;
+                    case '\n': sb.append("\\n"); break;
+                    case '\r': sb.append("\\r"); break;
+                    case '\t': sb.append("\\t"); break;
+                    case '\\': sb.append("\\\\"); break;
+                    default: sb.append(ch);
+                }
+            }
+            sb.append('"');
+            return sb.toString();
+        }
+        return value.toString();
+    }
+
+    public static String toString(BrwsrCtx c, Object obj, String prop) {
+        obj = getProperty(c, obj, prop);
+        return obj == null ? null : obj.toString();
+    }
+    public static Number toNumber(BrwsrCtx c, Object obj, String prop) {
+        obj = getProperty(c, obj, prop);
+        if (obj instanceof Character) {
+            obj = (int)(Character)obj;
+        }
+        if (!(obj instanceof Number)) {
+            obj = Double.NaN;
+        }
+        return (Number)obj;
+    }
+    public static <M> M toModel(BrwsrCtx c, Class<M> aClass, Object data, Object object) {
+        Technology<?> t = findTechnology(c);
+        Object o = t.toModel(aClass, data);
+        return aClass.cast(o);
+    }
+
+    public static boolean isSame(int a, int b) {
+        return a == b;
+    }
+
+    public static boolean isSame(double a, double b) {
+        return a == b;
+    }
+
+    public static boolean isSame(Object a, Object b) {
+        if (a == b) {
+            return true;
+        }
+        if (a == null || b == null) {
+            return false;
+        }
+        return a.equals(b);
+    }
+
+    public static int hashPlus(Object o, int h) {
+        return o == null ? h : h ^ o.hashCode();
+    }
+
+    public static <T> T extractValue(Class<T> type, Object val) {
+        if (Number.class.isAssignableFrom(type)) {
+            val = numberValue(val);
+        }
+        if (Boolean.class == type) {
+            val = boolValue(val);
+        }
+        if (String.class == type) {
+            val = stringValue(val);
+        }
+        if (Character.class == type) {
+            val = charValue(val);
+        }
+        if (Integer.class == type) {
+            val = val instanceof Number ? ((Number)val).intValue() : 0;
+        }
+        if (Long.class == type) {
+            val = val instanceof Number  ? ((Number)val).longValue() : 0;
+        }
+        if (Short.class == type) {
+            val = val instanceof Number ? ((Number)val).shortValue() : 0;
+        }
+        if (Byte.class == type) {
+            val = val instanceof Number ? ((Number)val).byteValue() : 0;
+        }
+        if (Double.class == type) {
+            val = val instanceof Number ? ((Number)val).doubleValue() : Double.NaN;
+        }
+        if (Float.class == type) {
+            val = val instanceof Number ? ((Number)val).floatValue() : Float.NaN;
+        }
+        return type.cast(val);
+    }
+
+    static boolean isNumeric(Object val) {
+        return ((val instanceof Integer) || (val instanceof Long) || (val instanceof Short) || (val instanceof Byte));
+    }
+
+    public static String stringValue(Object val) {
+        if (val instanceof Boolean) {
+            return ((Boolean)val ? "true" : "false");
+        }
+        if (isNumeric(val)) {
+            return Long.toString(((Number)val).longValue());
+        }
+        if (val instanceof Float) {
+            return Float.toString((Float)val);
+        }
+        if (val instanceof Double) {
+            return Double.toString((Double)val);
+        }
+        return (String)val;
+    }
+
+    public static Number numberValue(Object val) {
+        if (val instanceof String) {
+            try {
+                return Double.valueOf((String)val);
+            } catch (NumberFormatException ex) {
+                return Double.NaN;
+            }
+        }
+        if (val instanceof Boolean) {
+            return (Boolean)val ? 1 : 0;
+        }
+        return (Number)val;
+    }
+
+    public static Character charValue(Object val) {
+        if (val instanceof Number) {
+            return Character.toChars(numberValue(val).intValue())[0];
+        }
+        if (val instanceof Boolean) {
+            return (Boolean)val ? (char)1 : (char)0;
+        }
+        if (val instanceof String) {
+            String s = (String)val;
+            return s.isEmpty() ? (char)0 : s.charAt(0);
+        }
+        return (Character)val;
+    }
+
+    public static Boolean boolValue(Object val) {
+        if (val instanceof String) {
+            return Boolean.parseBoolean((String)val);
+        }
+        if (val instanceof Number) {
+            return numberValue(val).doubleValue() != 0.0;
+        }
+
+        return Boolean.TRUE.equals(val);
+    }
+
+    public static Object find(Object object, Bindings model) {
+        if (object == null) {
+            return null;
+        }
+        if (object instanceof JSONList) {
+            return ((JSONList<?>) object).koData();
+        }
+        if (object instanceof Collection) {
+            return JSONList.koData((Collection<?>) object, model);
+        }
+        if (
+            object instanceof String ||
+            object instanceof Boolean ||
+            object instanceof Number ||
+            object instanceof Character ||
+            object instanceof Enum<?>
+        ) {
+            return object;
+        }
+        Proto proto = findProto(object);
+        if (proto == null) {
+            return null;
+        }
+        final Bindings b = PropertyBindingAccessor.getBindings(proto, true, null);
+        return b == null ? null : b.koData();
+    }
+
+    private static Proto findProto(Object object) {
+        Proto.Type<?> type = JSON.findType(object.getClass());
+        if (type == null) {
+            return null;
+        }
+        final Proto proto = PropertyBindingAccessor.protoFor(type, object);
+        return proto;
+    }
+
+    public static Object find(Object object) {
+        return find(object, null);
+    }
+
+    public static void applyBindings(Object object, String id) {
+        final Proto proto = findProto(object);
+        if (proto == null) {
+            throw new IllegalArgumentException("Not a model: " + object.getClass());
+        }
+        proto.applyBindings(id);
+    }
+
+    public static abstract class WS {
+        private WS() {
+        }
+
+        public abstract void send(BrwsrCtx ctx, String headers, String url, Object model);
+        public static <Socket> WS create(WSTransfer<Socket> t, RcvrJSON r) {
+            return new WSImpl<Socket>(t, r);
+        }
+    }
+
+    private static final class WSImpl<Socket> extends WS {
+
+        private final WSTransfer<Socket> trans;
+        private final RcvrJSON rcvr;
+        private Socket socket;
+        private String prevURL;
+
+        private WSImpl(WSTransfer<Socket> trans, RcvrJSON rcvr) {
+            this.trans = trans;
+            this.rcvr = rcvr;
+        }
+
+        @Override
+        public void send(BrwsrCtx ctx, String headers, String url, Object data) {
+            Socket s = socket;
+            if (s == null) {
+                if (data != null) {
+                    throw new IllegalStateException("WebSocket is not opened yet. Call with null data, was: " + data);
+                }
+                JSONCall call = PropertyBindingAccessor.createCall(ctx, rcvr, headers, url, null, "WebSocket", null);
+                socket = trans.open(url, call);
+                prevURL = url;
+                return;
+            }
+            if (data == null) {
+                trans.close(s);
+                socket = null;
+                return;
+            }
+            if (!prevURL.equals(url)) {
+                throw new IllegalStateException(
+                    "Can't call to different URL " + url + " was: " + prevURL + "!"
+                    + " Close the socket by calling it will null data first!"
+                );
+            }
+            JSONCall call = PropertyBindingAccessor.createCall(ctx, rcvr, headers, prevURL, null, "WebSocket", data);
+            trans.send(s, call);
+        }
+
+    }
+
+    private static final Map<Class,Proto.Type<?>> modelTypes;
+    static {
+        modelTypes = new HashMap<Class, Proto.Type<?>>();
+    }
+    public static void register(Class c, Proto.Type<?> type) {
+        modelTypes.put(c, type);
+    }
+
+    public static boolean isModel(Class<?> clazz) {
+        return findType(clazz) != null;
+    }
+
+    static Proto.Type<?> findType(Class<?> clazz) {
+        for (int i = 0; i < 2; i++) {
+            Proto.Type<?> from = modelTypes.get(clazz);
+            if (from == null) {
+                initClass(clazz);
+            } else {
+                return from;
+            }
+        }
+        return null;
+    }
+
+    public static <Model> Model bindTo(Model model, BrwsrCtx c) {
+        Proto.Type<Model> from = (Proto.Type<Model>) findType(model.getClass());
+        if (from == null) {
+            throw new IllegalArgumentException();
+        }
+        return PropertyBindingAccessor.clone(from, model, c);
+    }
+
+    public static <T> T readStream(BrwsrCtx c, Class<T> modelClazz, InputStream data, Collection<? super T> collectTo)
+    throws IOException {
+        Transfer tr = findTransfer(c);
+        Object rawJSON = tr.toJSON((InputStream)data);
+        if (rawJSON instanceof Object[]) {
+            final Object[] arr = (Object[])rawJSON;
+            if (collectTo != null) {
+                for (int i = 0; i < arr.length; i++) {
+                    collectTo.add(read(c, modelClazz, arr[i]));
+                }
+                return null;
+            }
+            if (arr.length == 0) {
+                throw new EOFException("Recieved an empty array");
+            }
+            rawJSON = arr[0];
+        }
+        T res = read(c, modelClazz, rawJSON);
+        if (collectTo != null) {
+            collectTo.add(res);
+        }
+        return res;
+    }
+    public static <T> T read(BrwsrCtx c, Class<T> modelClazz, Object data) {
+        if (data == null) {
+            return null;
+        }
+        if (modelClazz == String.class) {
+            return modelClazz.cast(data.toString());
+        }
+        for (int i = 0; i < 2; i++) {
+            Proto.Type<?> from = modelTypes.get(modelClazz);
+            if (from == null) {
+                initClass(modelClazz);
+            } else {
+                return modelClazz.cast(PropertyBindingAccessor.readFrom(from, c, data));
+            }
+        }
+        throw new NullPointerException();
+    }
+    static void initClass(Class<?> modelClazz) {
+        try {
+            // try to resolve the class
+            ClassLoader l;
+            try {
+                l = modelClazz.getClassLoader();
+            } catch (SecurityException ex) {
+                l = null;
+            }
+            if (l != null) {
+                Class.forName(modelClazz.getName(), true, l);
+            }
+            modelClazz.newInstance();
+        } catch (Exception ex) {
+            // ignore and try again
+        }
+    }
+
+    private static final class EmptyTech
+    implements Technology<Object>, Transfer, WSTransfer<Void> {
+        private static final EmptyTech EMPTY = new EmptyTech();
+
+        @Override
+        public Object wrapModel(Object model) {
+            return model;
+        }
+
+        @Override
+        public void valueHasMutated(Object data, String propertyName) {
+        }
+
+        @Override
+        public void bind(PropertyBinding b, Object model, Object data) {
+        }
+
+        @Override
+        public void expose(FunctionBinding fb, Object model, Object d) {
+        }
+
+        @Override
+        public void applyBindings(Object data) {
+        }
+
+        @Override
+        public Object wrapArray(Object[] arr) {
+            return arr;
+        }
+
+        @Override
+        public void extract(Object obj, String[] props, Object[] values) {
+            for (int i = 0; i < values.length; i++) {
+                values[i] = null;
+            }
+        }
+
+        @Override
+        public void loadJSON(JSONCall call) {
+            call.notifyError(new UnsupportedOperationException());
+        }
+
+        @Override
+        public <M> M toModel(Class<M> modelClass, Object data) {
+            return modelClass.cast(data);
+        }
+
+        @Override
+        public Object toJSON(InputStream is) throws IOException {
+            throw new IOException("Not supported");
+        }
+
+        @Override
+        public void runSafe(Runnable r) {
+            r.run();
+        }
+
+        @Override
+        public Void open(String url, JSONCall onReply) {
+            onReply.notifyError(new UnsupportedOperationException("WebSockets not supported!"));
+            return null;
+        }
+
+        @Override
+        public void send(Void socket, JSONCall data) {
+        }
+
+        @Override
+        public void close(Void socket) {
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/main/java/org/netbeans/html/json/impl/JSONList.java
----------------------------------------------------------------------
diff --git a/json/src/main/java/org/netbeans/html/json/impl/JSONList.java b/json/src/main/java/org/netbeans/html/json/impl/JSONList.java
new file mode 100644
index 0000000..2a602cc
--- /dev/null
+++ b/json/src/main/java/org/netbeans/html/json/impl/JSONList.java
@@ -0,0 +1,257 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.json.impl;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Iterator;
+import org.netbeans.html.json.spi.Proto;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public final class JSONList<T> extends ArrayList<T> {
+    private final Proto proto;
+    private final String name;
+    private final String[] deps;
+    private final int index;
+
+    public JSONList(Proto proto, String name, int changeIndex, String... deps) {
+        this.proto = proto;
+        this.name = name;
+        this.deps = deps;
+        this.index = changeIndex;
+    }
+    
+    public void init(Object values) {
+        int len;
+        if (values == null || (len = Array.getLength(values)) == 0) {
+            return;
+        }
+        for (int i = 0; i < len; i++) {
+            Object data = Array.get(values, i);
+            super.add((T)data);
+        }
+    }
+    public static <T> void init(Collection<T> to, Object values) {
+        int len;
+        if (values == null || (len = Array.getLength(values)) == 0) {
+            return;
+        }
+        for (int i = 0; i < len; i++) {
+            Object data = Array.get(values, i);
+            to.add((T)data);
+        }
+    }
+    
+    @Override
+    public boolean add(T e) {
+        prepareChange();
+        boolean ret = super.add(e);
+        notifyChange();
+        return ret;
+    }
+
+    @Override
+    public boolean addAll(Collection<? extends T> c) {
+        prepareChange();
+        boolean ret = super.addAll(c);
+        notifyChange();
+        return ret;
+    }
+
+    @Override
+    public boolean addAll(int index, Collection<? extends T> c) {
+        prepareChange();
+        boolean ret = super.addAll(index, c);
+        notifyChange();
+        return ret;
+    }
+
+    public void fastReplace(Collection<? extends T> c) {
+        prepareChange();
+        super.clear();
+        super.addAll(c);
+        notifyChange();
+    }
+
+    @Override
+    public boolean remove(Object o) {
+        prepareChange();
+        boolean ret = super.remove(o);
+        notifyChange();
+        return ret;
+    }
+
+    @Override
+    public void clear() {
+        prepareChange();
+        super.clear();
+        notifyChange();
+    }
+
+    @Override
+    public boolean removeAll(Collection<?> c) {
+        prepareChange();
+        boolean ret = super.removeAll(c);
+        notifyChange();
+        return ret;
+    }
+
+    public void sort(Comparator<? super T> c) {
+        Object[] arr = this.toArray();
+        Arrays.sort(arr, (Comparator<Object>) c);
+        for (int i = 0; i < arr.length; i++) {
+            super.set(i, (T) arr[i]);
+        }
+        notifyChange();
+    }
+
+    @Override
+    public boolean retainAll(Collection<?> c) {
+        prepareChange();
+        boolean ret = super.retainAll(c);
+        notifyChange();
+        return ret;
+    }
+
+    @Override
+    public T set(int index, T element) {
+        prepareChange();
+        T ret = super.set(index, element);
+        notifyChange();
+        return ret;
+    }
+
+    @Override
+    public void add(int index, T element) {
+        prepareChange();
+        super.add(index, element);
+        notifyChange();
+    }
+
+    @Override
+    public T remove(int index) {
+        prepareChange();
+        T ret = super.remove(index);
+        notifyChange();
+        return ret;
+    }
+
+    @Override
+    protected void removeRange(int fromIndex, int toIndex) {
+        super.removeRange(fromIndex, toIndex);
+        notifyChange();
+    }
+
+    @Override
+    public String toString() {
+        Iterator<T> it = iterator();
+        if (!it.hasNext()) {
+            return "[]";
+        }
+        String sep = "";
+        StringBuilder sb = new StringBuilder();
+        sb.append('[');
+        while (it.hasNext()) {
+            T t = it.next();
+            sb.append(sep);
+            sb.append(JSON.toJSON(t));
+            sep = ",";
+        }
+        sb.append(']');
+        return sb.toString();
+    }
+
+    private void prepareChange() {
+        if (index == Integer.MIN_VALUE) {
+            try {
+                proto.initTo(null, null);
+            } catch (IllegalStateException ex) {
+                throw new UnsupportedOperationException();
+            }
+        }
+    }
+
+    private void notifyChange() {
+        proto.getContext().execute(new Runnable() {
+            @Override
+            public void run() {
+                Bindings m = PropertyBindingAccessor.getBindings(proto, false, null);
+                if (m != null) {
+                    m.valueHasMutated(name, null, JSONList.this);
+                    for (String dependant : deps) {
+                        m.valueHasMutated(dependant, null, null);
+                    }
+                    if (index >= 0) {
+                        PropertyBindingAccessor.notifyProtoChange(proto, index);
+                    }
+                }
+            }
+        });
+    }
+
+    @Override
+    public JSONList clone() {
+        throw new UnsupportedOperationException();
+    }
+
+    static final Object koData(Collection<?> c, Bindings m) {
+        Object[] arr = c.toArray(new Object[c.size()]);
+        for (int i = 0; i < arr.length; i++) {
+            Object r = JSON.find(arr[i], m);
+            if (r != null) {
+                arr[i] = r;
+            }
+        }
+        return m.wrapArray(arr);
+    }
+
+    final Object koData() {
+        return koData(this, PropertyBindingAccessor.getBindings(proto, true, null));
+    }
+}


[05/24] incubator-netbeans-html4j git commit: [INFRA-15006] Initial donation of HTML/Java NetBeans API. Equivalent to the content of html4j-donation-review.zip donated as part of ApacheNetBeansDonation1.zip with SHA256 being 7f2ca0f61953a190613c9a0fbcc1b

Posted by jt...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/org/netbeans/html/json/impl/OnReceiveTest.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/org/netbeans/html/json/impl/OnReceiveTest.java b/json/src/test/java/org/netbeans/html/json/impl/OnReceiveTest.java
new file mode 100644
index 0000000..f2a52bb
--- /dev/null
+++ b/json/src/test/java/org/netbeans/html/json/impl/OnReceiveTest.java
@@ -0,0 +1,180 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.json.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+import net.java.html.BrwsrCtx;
+import net.java.html.json.Models;
+import net.java.html.json.Person;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.json.spi.JSONCall;
+import org.netbeans.html.json.spi.Transfer;
+import static org.testng.Assert.*;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class OnReceiveTest {
+    @Test public void performJSONCall() {
+        MockTrans mt = new MockTrans();
+        BrwsrCtx ctx = Contexts.newBuilder().register(Transfer.class, mt, 1).build();
+        
+        Employee e = Models.bind(new Employee(), ctx);
+        e.setCall(null);
+        Person p = new Person();
+        
+        mt.result = new HashMap<String, String>();
+        mt.result.put("firstName", "Jarda");
+        mt.result.put("lastName", "Tulach");
+        e.changePersonalities(1, 2.0, "3", p);
+        final Call c = e.getCall();
+        assertNotNull(c, "A call has been made");
+        assertEquals(c.getI(), 1);
+        assertEquals(c.getD(), 2.0);
+        assertEquals(c.getS(), "3");
+        assertEquals(c.getP(), p);
+        assertEquals(c.getData().size(), 1, "One result sent over wire");
+        assertEquals(c.getData().get(0).getFirstName(), "Jarda");
+        assertEquals(c.getData().get(0).getLastName(), "Tulach");
+    }
+
+    @Test public void performErrorJSONCallNoHandling() {
+        MockTrans mt = new MockTrans();
+        mt.err = new Exception("Error");
+        BrwsrCtx ctx = Contexts.newBuilder().register(Transfer.class, mt, 1).build();
+
+        Employee e = Models.bind(new Employee(), ctx);
+        e.setCall(null);
+        Person p = new Person();
+
+        mt.result = new HashMap<String, String>();
+        mt.result.put("firstName", "Jarda");
+        mt.result.put("lastName", "Tulach");
+        e.changePersonalities(1, 2.0, "3", p);
+        final Call c = e.getCall();
+        assertNull(c, "Error has been swallowed");
+    }
+    
+    @Test public void performErrorJSONCall() {
+        MockTrans mt = new MockTrans();
+        mt.err = new Exception("Error");
+        BrwsrCtx ctx = Contexts.newBuilder().register(Transfer.class, mt, 1).build();
+
+        Employee e = Models.bind(new Employee(), ctx);
+        e.setCall(null);
+        Person p = new Person();
+
+        mt.result = new HashMap<String, String>();
+        mt.result.put("firstName", "Jarda");
+        mt.result.put("lastName", "Tulach");
+        e.changePersonalitiesWithEx(1, 2.0, "3", p);
+        final Call c = e.getCall();
+        assertNotNull(c, "A call has been made");
+        assertTrue(c.getData().isEmpty(), "No data provided");
+
+        assertEquals(c.getI(), -1);
+        assertEquals(c.getD(), -1.0);
+        assertEquals(c.getS(), null);
+        assertEquals(c.getP(), null);
+    }
+
+    @Test public void performErrorWithValuesJSONCall() {
+        MockTrans mt = new MockTrans();
+        mt.err = new Exception("Error");
+        BrwsrCtx ctx = Contexts.newBuilder().register(Transfer.class, mt, 1).build();
+
+        Employee e = Models.bind(new Employee(), ctx);
+        e.setCall(null);
+        Person p = new Person();
+
+        mt.result = new HashMap<String, String>();
+        mt.result.put("firstName", "Jarda");
+        mt.result.put("lastName", "Tulach");
+        e.changePersonalitiesWithParam(1, 2.0, "3", p);
+        final Call c = e.getCall();
+        assertNotNull(c, "A call has been made");
+        assertTrue(c.getData().isEmpty(), "No data provided");
+
+        assertEquals(c.getI(), 1);
+        assertEquals(c.getD(), 2.0);
+        assertEquals(c.getS(), "3");
+        assertEquals(c.getP(), p);
+    }
+
+    
+    public static class MockTrans implements Transfer {
+        Map<String,String> result;
+        Exception err;
+        
+        @Override
+        public void extract(Object obj, String[] props, Object[] values) {
+            assertTrue(obj instanceof Map, "It is a map: " + obj);
+            Map<?,?> mt = (Map<?,?>) obj;
+            for (int i = 0; i < props.length; i++) {
+                values[i] = mt.get(props[i]);
+            }
+        }
+
+        @Override
+        public Object toJSON(InputStream is) throws IOException {
+            throw new IOException();
+        }
+
+        @Override
+        public void loadJSON(JSONCall call) {
+            Object r = result;
+            assertNotNull(r, "We need a reply!");
+            result = null;
+            if (err != null) {
+                call.notifyError(err);
+            } else {
+                call.notifySuccess(r);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/org/netbeans/html/json/impl/ParallelChangeTest.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/org/netbeans/html/json/impl/ParallelChangeTest.java b/json/src/test/java/org/netbeans/html/json/impl/ParallelChangeTest.java
new file mode 100644
index 0000000..66c89e3
--- /dev/null
+++ b/json/src/test/java/org/netbeans/html/json/impl/ParallelChangeTest.java
@@ -0,0 +1,199 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.json.impl;
+
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import net.java.html.BrwsrCtx;
+import net.java.html.json.ComputedProperty;
+import net.java.html.json.Model;
+import net.java.html.json.Models;
+import net.java.html.json.Property;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.json.impl.DeepChangeTest.One;
+import org.netbeans.html.json.spi.Technology;
+import org.netbeans.html.json.spi.Transfer;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import static org.testng.Assert.assertEquals;
+
+public class ParallelChangeTest {
+    private DeepChangeTest.MapTechnology t;
+    private BrwsrCtx c;
+
+    @BeforeMethod public void initTechnology() {
+        t = new DeepChangeTest.MapTechnology();
+        c = Contexts.newBuilder().register(Technology.class, t, 1).
+            register(Transfer.class, t, 1).build();
+    }
+
+    @Test
+    public void multipleValues() throws InterruptedException {
+        doTest(true);
+    }
+
+    @Test
+    public void singleValue() throws InterruptedException {
+        doTest(false);
+    }
+
+    private void doTest(boolean multipleValues) throws InterruptedException {
+        class Test implements Runnable {
+            final int offset;
+            final Depending dep;
+            private Error error;
+
+            public Test(int index, Depending dep) {
+                this.offset = index;
+                this.dep = dep;
+            }
+
+            @Override
+            public void run() {
+                try {
+                    int value = dep.getValuePlusAdd();
+                    assertEquals(value, offset + 11, "Offset " + offset + " plus one plus ten");
+                } catch (Error err) {
+                    this.error = err;
+                }
+            }
+
+            private void assertException() {
+                if (error != null) {
+                    throw error;
+                }
+            }
+        }
+
+        Depending[] deps = new Depending[2];
+        BlockingValue[] values = new BlockingValue[deps.length];
+        CountDownLatch blockInCall = new CountDownLatch(deps.length);
+        Test[] runs = new Test[deps.length];
+        ExecutorService exec = Executors.newFixedThreadPool(deps.length);
+        for (int i = 0; i < deps.length; i++) {
+            if (multipleValues) {
+                values[i] = BlockingValueCntrl.create(c);
+            } else {
+                values[i] = i == 0 ? BlockingValueCntrl.create(c) : values[0];
+            }
+            deps[i] = DependingCntrl.create(c, values[i], 10);
+            runs[i] = new Test(0, deps[i]);
+        }
+        BlockingValueCntrl.initialize(blockInCall);
+        for (int i = 0; i < deps.length; i++) {
+            exec.execute(runs[i]);
+        }
+
+        exec.awaitTermination(1, TimeUnit.SECONDS);
+
+        for (int i = 0; i < deps.length; i++) {
+            Map raw = (Map) Models.toRaw(deps[i]);
+            One value = (One) raw.get("valuePlusAdd");
+            value.assertNoChange("No changes yet for index " + i);
+        }
+
+        for (int i = 0; i < deps.length; i++) {
+            runs[i].assertException();
+            values[i].setValue(30);
+        }
+
+        for (int i = 0; i < deps.length; i++) {
+            Map raw = (Map) Models.toRaw(deps[i]);
+            One value = (One) raw.get("valuePlusAdd");
+            value.assertChange("A change for index " + i);
+        }
+
+        for (int i = 0; i < deps.length; i++) {
+            assertEquals(deps[i].getValuePlusAdd(), 41, "[" + i + "] = 0 plus 30 plus one plus 10");
+        }
+    }
+
+    @Model(className="BlockingValue", properties = {
+        @Property(name = "value", type = int.class)
+    })
+    static class BlockingValueCntrl {
+        private static CountDownLatch latch;
+
+        static void initialize(CountDownLatch l) {
+            latch = l;
+        }
+
+        @ComputedProperty
+        static int plusOne(int value)  {
+            if (latch != null) {
+                latch.countDown();
+                try {
+                    latch.await();
+                } catch (InterruptedException ex) {
+                    throw new IllegalStateException(ex);
+                }
+            }
+            return value + 1;
+        }
+
+        static BlockingValue create(BrwsrCtx c) {
+            return Models.bind(new BlockingValue(), c);
+        }
+    }
+
+    @Model(className = "Depending", properties = {
+        @Property(name = "add", type = int.class),
+        @Property(name = "dep", type = BlockingValue.class)
+    })
+    static class DependingCntrl {
+        @ComputedProperty
+        static int valuePlusAdd(BlockingValue dep, int add) {
+            return dep.getPlusOne() + add;
+        }
+
+        static Depending create(BrwsrCtx c, BlockingValue value, int add) {
+            Depending d = Models.bind(new Depending(add, null), c);
+            d.setDep(value);
+            return d;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/org/netbeans/html/json/impl/ToDoTest.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/org/netbeans/html/json/impl/ToDoTest.java b/json/src/test/java/org/netbeans/html/json/impl/ToDoTest.java
new file mode 100644
index 0000000..e12d443
--- /dev/null
+++ b/json/src/test/java/org/netbeans/html/json/impl/ToDoTest.java
@@ -0,0 +1,132 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.json.impl;
+
+import java.util.List;
+import java.util.Map;
+import net.java.html.BrwsrCtx;
+import net.java.html.json.ComputedProperty;
+import net.java.html.json.Model;
+import net.java.html.json.Models;
+import net.java.html.json.Property;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.json.spi.Technology;
+import org.netbeans.html.json.spi.Transfer;
+import org.netbeans.html.json.impl.DeepChangeTest.MapTechnology;
+import org.netbeans.html.json.impl.DeepChangeTest.One;
+import static org.testng.Assert.*;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+@Model(className = "TodoUI", properties = {
+    @Property(name = "todos", type = Todo.class, array = true),
+    @Property(name = "todoText", type = String.class)
+})
+public class ToDoTest {
+    @Model(className = "Todo", properties = {
+        @Property(name = "text", type = String.class),
+        @Property(name = "done", type = boolean.class)
+    })
+    static class ItemCtrl {
+    }
+
+    @ComputedProperty
+    static int remaining(
+        List<Todo> todos, String todoText
+    ) {
+        int count = 0;
+        for (Todo d : todos) {
+            if (!d.isDone()) {
+                count++;
+            }
+        }
+        return count;
+    }
+    
+    private MapTechnology t;
+    private BrwsrCtx c;
+
+    @BeforeMethod
+    public void initTechnology() {
+        t = new MapTechnology();
+        c = Contexts.newBuilder().register(Technology.class, t, 1).
+                register(Transfer.class, t, 1).build();
+    }
+    
+    
+    @Test public void checkAndUncheckFirstItem() throws Exception {
+        TodoUI ui = Models.bind(
+                new TodoUI(
+                    null,
+                    new Todo("First", false),
+                    new Todo("2nd", true),
+                    new Todo("Third", false)
+                ), c);
+        Models.applyBindings(ui);
+
+        Map m = (Map) Models.toRaw(ui);
+        Object v = m.get("remaining");
+        assertNotNull(v, "Value should be in the map");
+        assertEquals(v.getClass(), One.class, "It is instance of One");
+        One o = (One) v;
+        assertEquals(o.changes, 0, "No changes so far");
+        assertTrue(o.pb.isReadOnly(), "Derived property");
+        assertEquals(o.get(), 2);
+
+        ui.getTodos().get(0).setDone(true);
+
+        assertEquals(o.get(), 1);
+        assertEquals(o.changes, 1, "One change so far");
+
+        ui.getTodos().get(0).setDone(false);
+
+        assertEquals(o.get(), 2);
+        assertEquals(o.changes, 2, "2nd change so far");
+        
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko-felix-test/pom.xml
----------------------------------------------------------------------
diff --git a/ko-felix-test/pom.xml b/ko-felix-test/pom.xml
new file mode 100644
index 0000000..591127e
--- /dev/null
+++ b/ko-felix-test/pom.xml
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2016 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<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">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.netbeans.html</groupId>
+        <artifactId>pom</artifactId>
+        <version>2.0-SNAPSHOT</version>
+    </parent>
+    <name>KO Tests in Felix OSGi Container</name>
+    <artifactId>ko-felix-test</artifactId>
+    <packaging>bundle</packaging>
+    <description>Runs the TCK for Knockout in Felix OSGi Container</description>
+    <properties>
+        <netbeans.compile.on.save>none</netbeans.compile.on.save>
+    </properties>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.netbeans.html</groupId>
+                <artifactId>html4j-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-deploy-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-failsafe-plugin</artifactId>
+                <configuration>
+                    <additionalClasspathElements>
+                        <additionalClasspathElement>${project.build.directory}/${project.build.finalName}.jar</additionalClasspathElement>
+                    </additionalClasspathElements>
+                    <forkMode>always</forkMode>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>integration-test</goal>
+                            <goal>verify</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+    <dependencies>
+        <dependency>
+            <groupId>com.oracle</groupId>
+            <artifactId>javafx</artifactId>
+            <version>2.2</version>
+            <scope>system</scope>
+            <systemPath>${jfxrt.jar}</systemPath>
+        </dependency>
+        <dependency>
+            <groupId>de.twentyeleven.skysail</groupId>
+            <artifactId>org.json-osgi</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.netbeans.html</groupId>
+            <artifactId>net.java.html.json</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.testng</groupId>
+            <artifactId>testng</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>net.java.html.json.tck</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.netbeans.api</groupId>
+            <artifactId>org-openide-util-lookup</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.netbeans.html</groupId>
+            <artifactId>net.java.html.boot</artifactId>
+            <version>${project.version}</version>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ko4j</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>net.java.html.boot.fx</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.grizzly</groupId>
+            <artifactId>grizzly-http-server</artifactId>
+            <version>${grizzly.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.grizzly</groupId>
+            <artifactId>grizzly-websockets-server</artifactId>
+            <version>${grizzly.version}</version>
+            <scope>test</scope>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.grizzly</groupId>
+            <artifactId>grizzly-http-servlet</artifactId>
+            <version>${grizzly.version}</version>
+            <scope>test</scope>
+        </dependency>    
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.framework</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.aries.spifly</groupId>
+            <artifactId>org.apache.aries.spifly.dynamic.bundle</artifactId>
+            <version>1.0.4</version>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.log</artifactId>
+            <version>1.3.0</version>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko-felix-test/src/main/java/org/netbeans/html/ko/felix/test/KnockoutFelixTCKImpl.java
----------------------------------------------------------------------
diff --git a/ko-felix-test/src/main/java/org/netbeans/html/ko/felix/test/KnockoutFelixTCKImpl.java b/ko-felix-test/src/main/java/org/netbeans/html/ko/felix/test/KnockoutFelixTCKImpl.java
new file mode 100644
index 0000000..c085ff3
--- /dev/null
+++ b/ko-felix-test/src/main/java/org/netbeans/html/ko/felix/test/KnockoutFelixTCKImpl.java
@@ -0,0 +1,283 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.ko.felix.test;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import net.java.html.BrwsrCtx;
+import net.java.html.boot.BrowserBuilder;
+import net.java.html.js.JavaScriptBody;
+import org.netbeans.html.boot.spi.Fn;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.json.spi.Technology;
+import org.netbeans.html.json.spi.Transfer;
+import org.netbeans.html.json.tck.KnockoutTCK;
+import org.openide.util.lookup.ServiceProvider;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+@ServiceProvider(service = KnockoutTCK.class)
+public class KnockoutFelixTCKImpl extends KnockoutTCK implements Callable<Class[]> {
+    
+    private static Fn.Presenter browserContext;
+
+    public static Class loadOSGiClass(String name, BundleContext ctx) throws Exception {
+        for (Bundle b : ctx.getBundles()) {
+            try {
+                Class<?> osgiClass = b.loadClass(name);
+                if (osgiClass != null && osgiClass.getClassLoader() != ClassLoader.getSystemClassLoader()) {
+                    return osgiClass;
+                }
+            } catch (ClassNotFoundException cnfe) {
+                // go on
+            }
+        }
+        throw new IllegalStateException("Cannot load " + name + " from the OSGi container!");
+    }
+
+    @Override
+    public Class[] call() throws Exception {
+        return testClasses();
+    }
+    
+    public static void start(String callBackClass, URI server, final boolean useAllClassloader) throws Exception {
+        final BrowserBuilder bb = BrowserBuilder.newBrowser().loadClass(KnockoutFelixTCKImpl.class).
+            loadPage(server.toString()).
+            invoke("initialized", callBackClass);
+
+        Executors.newSingleThreadExecutor().submit(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    Bundle[] arr = FrameworkUtil.getBundle(BrowserBuilder.class).getBundleContext().getBundles();
+                    if (useAllClassloader) {
+                        final ClassLoader osgiClassLoader = new AllBundlesLoader(arr);
+                        bb.classloader(osgiClassLoader);
+                    }
+                    bb.showAndWait();
+                } catch (Throwable t) {
+                    t.printStackTrace();
+                }
+            }
+        });
+    }
+
+    public static void initialized(String... args) throws Exception {
+        Bundle bundle = FrameworkUtil.getBundle(KnockoutFelixTCKImpl.class);
+        if (bundle == null) {
+            throw new IllegalStateException(
+                "Should be loaded from a bundle. But was: " + KnockoutFelixTCKImpl.class.getClassLoader()
+            );
+        }
+        Class<?> classpathClass = ClassLoader.getSystemClassLoader().loadClass(args[0]);
+        Method m = classpathClass.getMethod("initialized", Class.class, Object.class);
+        browserContext = Fn.activePresenter();
+        m.invoke(null, KnockoutFelixTCKImpl.class, browserContext);
+    }
+    
+    @Override
+    public BrwsrCtx createContext() {
+        try {
+            Contexts.Builder cb = Contexts.newBuilder().
+                register(Technology.class, (Technology)osgiInstance("KOTech"), 10).
+                register(Transfer.class, (Transfer)osgiInstance("KOTransfer"), 10).
+                register(Executor.class, (Executor)browserContext, 10);
+//        if (fx.areWebSocketsSupported()) {
+//            cb.register(WSTransfer.class, fx, 10);
+//        }
+            return cb.build();
+        } catch (Exception ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+
+    private Object osgiInstance(String simpleName) throws IllegalAccessException, SecurityException, IllegalArgumentException, Exception, NoSuchMethodException, InstantiationException, InvocationTargetException {
+        Class<?> fxCls = loadOSGiClass(
+                "org.netbeans.html.ko4j." + simpleName,
+                FrameworkUtil.getBundle(KnockoutFelixTCKImpl.class).getBundleContext()
+        );
+        final Constructor<?> cnstr = fxCls.getDeclaredConstructor();
+        cnstr.setAccessible(true);
+        Object fx = cnstr.newInstance();
+        return fx;
+    }
+
+    @Override
+    public Object createJSON(Map<String, Object> values) {
+        Object json = createObj();
+        for (Map.Entry<String, Object> entry : values.entrySet()) {
+            putObj(json, entry.getKey(), entry.getValue());
+        }
+        return json;
+    }
+    
+    @JavaScriptBody(args = {  }, body = "return {};")
+    private static native Object createObj();
+    @JavaScriptBody(args = { "obj", "prop", "val" }, body = "obj[prop] = val;")
+    private static native void putObj(Object obj, String prop, Object val);
+
+    @Override
+    @JavaScriptBody(args = { "s", "args" }, body = ""
+        + "var f = new Function(s); "
+        + "return f.apply(null, args);"
+    )
+    public native Object executeScript(String script, Object[] arguments);
+
+    @JavaScriptBody(args = {  }, body = 
+          "var h;"
+        + "if (!!window && !!window.location && !!window.location.href)\n"
+        + "  h = window.location.href;\n"
+        + "else "
+        + "  h = null;"
+        + "return h;\n"
+    )
+    private static native String findBaseURL();
+    
+    @Override
+    public URI prepareURL(String content, String mimeType, String[] parameters) {
+        try {
+            final URL baseURL = new URL(findBaseURL());
+            StringBuilder sb = new StringBuilder();
+            sb.append("/dynamic?mimeType=").append(mimeType);
+            for (int i = 0; i < parameters.length; i++) {
+                sb.append("&param" + i).append("=").append(parameters[i]);
+            }
+            String mangle = content.replace("\n", "%0a")
+                .replace("\"", "\\\"").replace(" ", "%20");
+            sb.append("&content=").append(mangle);
+
+            URL query = new URL(baseURL, sb.toString());
+            URLConnection c = query.openConnection();
+            BufferedReader br = new BufferedReader(new InputStreamReader(c.getInputStream()));
+            URI connectTo = new URI(br.readLine());
+            return connectTo;
+        } catch (IOException ex) {
+            throw new IllegalStateException(ex);
+        } catch (URISyntaxException ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+
+    @Override
+    public boolean canFailWebSocketTest() {
+        return true;
+    }
+
+    private static final class AllBundlesLoader extends ClassLoader {
+        private final Bundle[] arr;
+
+        public AllBundlesLoader(Bundle[] arr) {
+            super(ClassLoader.getSystemClassLoader().getParent());
+            this.arr = arr;
+        }
+
+        @Override
+        public Class<?> loadClass(String name) throws ClassNotFoundException {
+            return loadClass(name, false);
+        }
+
+        @Override
+        protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+            ClassNotFoundException err = null;
+            for (Bundle b : arr) {
+                try {
+                    Class<?> cls = b.loadClass(name);
+                    if (FrameworkUtil.getBundle(cls) == b) {
+                        return cls;
+                    }
+                } catch (ClassNotFoundException ex) {
+                    err = ex;
+                }
+            }
+            throw err;
+        }
+
+        @Override
+        protected URL findResource(String name) {
+            for (Bundle b : arr) {
+                URL r = b.getResource(name);
+                if (r != null) {
+                    return r;
+                }
+            }
+            return null;
+        }
+
+        @Override
+        protected Enumeration<URL> findResources(String name) throws IOException {
+            List<URL> ret = new ArrayList<URL>();
+            for (Bundle b : arr) {
+                Enumeration<URL> en = b.getResources(name);
+                if (en != null) while (en.hasMoreElements()) {
+                    URL u = en.nextElement();
+                    ret.add(u);
+                }
+            }
+            return Collections.enumeration(ret);
+        }
+        
+        
+        
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko-felix-test/src/test/java/org/netbeans/html/ko/felix/test/DynamicHTTP.java
----------------------------------------------------------------------
diff --git a/ko-felix-test/src/test/java/org/netbeans/html/ko/felix/test/DynamicHTTP.java b/ko-felix-test/src/test/java/org/netbeans/html/ko/felix/test/DynamicHTTP.java
new file mode 100644
index 0000000..ce99dce
--- /dev/null
+++ b/ko-felix-test/src/test/java/org/netbeans/html/ko/felix/test/DynamicHTTP.java
@@ -0,0 +1,261 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.ko.felix.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.glassfish.grizzly.PortRange;
+import org.glassfish.grizzly.http.server.HttpHandler;
+import org.glassfish.grizzly.http.server.HttpServer;
+import org.glassfish.grizzly.http.server.NetworkListener;
+import org.glassfish.grizzly.http.server.Request;
+import org.glassfish.grizzly.http.server.Response;
+import org.glassfish.grizzly.http.server.ServerConfiguration;
+import org.glassfish.grizzly.websockets.WebSocket;
+import org.glassfish.grizzly.websockets.WebSocketAddOn;
+import org.glassfish.grizzly.websockets.WebSocketApplication;
+import org.glassfish.grizzly.websockets.WebSocketEngine;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+final class DynamicHTTP extends HttpHandler {
+    private static final Logger LOG = Logger.getLogger(DynamicHTTP.class.getName());
+    private static int resourcesCount;
+    private static List<Resource> resources;
+    private static ServerConfiguration conf;
+    private static HttpServer server;
+
+    private DynamicHTTP() {
+    }
+
+    static URI initServer() throws Exception {
+        server = HttpServer.createSimpleServer(null, new PortRange(8080, 65535));
+        final WebSocketAddOn addon = new WebSocketAddOn();
+        for (NetworkListener listener : server.getListeners()) {
+            listener.registerAddOn(addon);
+        }
+        resources = new ArrayList<Resource>();
+
+        conf = server.getServerConfiguration();
+        final DynamicHTTP dh = new DynamicHTTP();
+
+        conf.addHttpHandler(dh, "/");
+
+        server.start();
+
+        return pageURL("http", server, "/test.html");
+    }
+
+    @Override
+    public void service(Request request, Response response) throws Exception {
+        if ("/test.html".equals(request.getRequestURI())) {
+            response.setContentType("text/html");
+            final InputStream is = DynamicHTTP.class.getResourceAsStream("test.html");
+            copyStream(is, response.getOutputStream(), null);
+            return;
+        }
+        if ("/dynamic".equals(request.getRequestURI())) {
+            String mimeType = request.getParameter("mimeType");
+            List<String> params = new ArrayList<String>();
+            boolean webSocket = false;
+            for (int i = 0;; i++) {
+                String p = request.getParameter("param" + i);
+                if (p == null) {
+                    break;
+                }
+                if ("protocol:ws".equals(p)) {
+                    webSocket = true;
+                    continue;
+                }
+                params.add(p);
+            }
+            final String cnt = request.getParameter("content");
+            String mangle = cnt.replace("%20", " ").replace("%0A", "\n");
+            ByteArrayInputStream is = new ByteArrayInputStream(mangle.getBytes("UTF-8"));
+            URI url;
+            final Resource res = new Resource(is, mimeType, "/dynamic/res" + ++resourcesCount, params.toArray(new String[params.size()]));
+            if (webSocket) {
+                url = registerWebSocket(res);
+            } else {
+                url = registerResource(res);
+            }
+            response.getWriter().write(url.toString());
+            response.getWriter().write("\n");
+            return;
+        }
+
+        for (Resource r : resources) {
+            if (r.httpPath.equals(request.getRequestURI())) {
+                response.setContentType(r.httpType);
+                r.httpContent.reset();
+                String[] params = null;
+                if (r.parameters.length != 0) {
+                    params = new String[r.parameters.length];
+                    for (int i = 0; i < r.parameters.length; i++) {
+                        params[i] = request.getParameter(r.parameters[i]);
+                        if (params[i] == null) {
+                            if ("http.method".equals(r.parameters[i])) {
+                                params[i] = request.getMethod().toString();
+                            } else if ("http.requestBody".equals(r.parameters[i])) {
+                                Reader rdr = request.getReader();
+                                StringBuilder sb = new StringBuilder();
+                                for (;;) {
+                                    int ch = rdr.read();
+                                    if (ch == -1) {
+                                        break;
+                                    }
+                                    sb.append((char) ch);
+                                }
+                                params[i] = sb.toString();
+                            } else if (r.parameters[i].startsWith("http.header.")) {
+                                params[i] = request.getHeader(r.parameters[i].substring(12));
+                            }
+                        }
+                        if (params[i] == null) {
+                            params[i] = "null";
+                        }
+                    }
+                }
+
+                copyStream(r.httpContent, response.getOutputStream(), null, params);
+            }
+        }
+    }
+
+    private URI registerWebSocket(Resource r) {
+        WebSocketEngine.getEngine().register("", r.httpPath, new WS(r));
+        return pageURL("ws", server, r.httpPath);
+    }
+
+    private URI registerResource(Resource r) {
+        if (!resources.contains(r)) {
+            resources.add(r);
+            conf.addHttpHandler(this, r.httpPath);
+        }
+        return pageURL("http", server, r.httpPath);
+    }
+
+    private static URI pageURL(String proto, HttpServer server, final String page) {
+        NetworkListener listener = server.getListeners().iterator().next();
+        int port = listener.getPort();
+        try {
+            return new URI(proto + "://localhost:" + port + page);
+        } catch (URISyntaxException ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+
+    static final class Resource {
+
+        final InputStream httpContent;
+        final String httpType;
+        final String httpPath;
+        final String[] parameters;
+
+        Resource(InputStream httpContent, String httpType, String httpPath,
+            String[] parameters) {
+            httpContent.mark(Integer.MAX_VALUE);
+            this.httpContent = httpContent;
+            this.httpType = httpType;
+            this.httpPath = httpPath;
+            this.parameters = parameters;
+        }
+    }
+
+    static void copyStream(InputStream is, OutputStream os, String baseURL, String... params) throws IOException {
+        for (;;) {
+            int ch = is.read();
+            if (ch == -1) {
+                break;
+            }
+            if (ch == '$' && params.length > 0) {
+                int cnt = is.read() - '0';
+                if (baseURL != null && cnt == 'U' - '0') {
+                    os.write(baseURL.getBytes("UTF-8"));
+                } else {
+                    if (cnt >= 0 && cnt < params.length) {
+                        os.write(params[cnt].getBytes("UTF-8"));
+                    } else {
+                        os.write('$');
+                        os.write(cnt + '0');
+                    }
+                }
+            } else {
+                os.write(ch);
+            }
+        }
+    }
+
+    private static class WS extends WebSocketApplication {
+        private final Resource r;
+
+        private WS(Resource r) {
+            this.r = r;
+        }
+
+        @Override
+        public void onMessage(WebSocket socket, String text) {
+            try {
+                r.httpContent.reset();
+                ByteArrayOutputStream out = new ByteArrayOutputStream();
+                copyStream(r.httpContent, out, null, text);
+                String s = new String(out.toByteArray(), "UTF-8");
+                socket.send(s);
+            } catch (IOException ex) {
+                LOG.log(Level.WARNING, "Error processing message " + text, ex);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko-felix-test/src/test/java/org/netbeans/html/ko/felix/test/KOFx.java
----------------------------------------------------------------------
diff --git a/ko-felix-test/src/test/java/org/netbeans/html/ko/felix/test/KOFx.java b/ko-felix-test/src/test/java/org/netbeans/html/ko/felix/test/KOFx.java
new file mode 100644
index 0000000..acd21be
--- /dev/null
+++ b/ko-felix-test/src/test/java/org/netbeans/html/ko/felix/test/KOFx.java
@@ -0,0 +1,132 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.ko.felix.test;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import javafx.application.Platform;
+import org.testng.ITest;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public final class KOFx implements ITest, Runnable {
+    private final Object p;
+    private final Method m;
+    private Object result;
+    private Object inst;
+    private int count;
+    private final Class<?> itClass;
+
+    KOFx(Class<?> itClass, Object p, Method m) {
+        this.itClass = itClass;
+        this.p = p;
+        this.m = m;
+    }
+
+    @Override
+    public String getTestName() {
+        return m.getName();
+    }
+
+    @Test
+    public synchronized void executeTest() throws Exception {
+        if (result == null) {
+            Platform.runLater(this);
+            wait();
+        }
+        if (result instanceof Exception) {
+            throw (Exception)result;
+        }
+        if (result instanceof Error) {
+            throw (Error)result;
+        }
+    }
+
+    @Override
+    public synchronized void run() {
+        boolean notify = true;
+        Closeable a = null;
+        try {
+            a = (Closeable) itClass.getMethod("activateInOSGi", Object.class).invoke(null, p);
+            if (inst == null) {
+                inst = m.getDeclaringClass().newInstance();
+            }
+            result = m.invoke(inst);
+            if (result == null) {
+                result = this;
+            }
+        } catch (InvocationTargetException ex) {
+            Throwable r = ex.getTargetException();
+            if (r instanceof InterruptedException) {
+                if (count++ < 10000) {
+                    notify = false;
+                    try {
+                        Thread.sleep(100);
+                    } catch (Exception ex1) {
+                        // ignore and continue
+                    }
+                    Platform.runLater(this);
+                    return;
+                }
+            }
+            result = r;
+        } catch (Exception ex) {
+            result = ex;
+        } finally {
+            if (notify) {
+                notifyAll();
+            }
+            try {
+                if (a != null) a.close();
+            } catch (IOException ex) {
+                throw new IllegalStateException(ex);
+            }
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko-felix-test/src/test/java/org/netbeans/html/ko/felix/test/KnockoutFelixAriesIT.java
----------------------------------------------------------------------
diff --git a/ko-felix-test/src/test/java/org/netbeans/html/ko/felix/test/KnockoutFelixAriesIT.java b/ko-felix-test/src/test/java/org/netbeans/html/ko/felix/test/KnockoutFelixAriesIT.java
new file mode 100644
index 0000000..adcfaef
--- /dev/null
+++ b/ko-felix-test/src/test/java/org/netbeans/html/ko/felix/test/KnockoutFelixAriesIT.java
@@ -0,0 +1,258 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.ko.felix.test;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.ServiceLoader;
+import java.util.concurrent.Callable;
+import java.util.jar.JarFile;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.netbeans.html.boot.spi.Fn;
+import org.netbeans.html.json.tck.KOTest;
+import org.netbeans.html.json.tck.KnockoutTCK;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.launch.Framework;
+import org.osgi.framework.launch.FrameworkFactory;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.Factory;
+
+public class KnockoutFelixAriesIT {
+    private static final Logger LOG = Logger.getLogger(KnockoutFelixAriesIT.class.getName());
+    private static Framework framework;
+    private static File dir;
+    static Framework framework() throws Exception {
+        if (framework != null) {
+            return framework;
+        }
+        for (FrameworkFactory ff : ServiceLoader.load(FrameworkFactory.class)) {
+            
+            String basedir = System.getProperty("basedir");
+            assertNotNull("basedir preperty provided", basedir);
+            File target = new File(basedir, "target");
+            dir = new File(target, "osgi-aries");
+            dir.mkdirs();
+            
+            Map<String,String> config = new HashMap<String, String>();
+            config.put(Constants.FRAMEWORK_STORAGE, dir.getPath());
+            config.put(Constants.FRAMEWORK_STORAGE_CLEAN, "true");
+            config.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, "sun.misc,"
+                + "javafx.application,"
+                + "javafx.beans,"
+                + "javafx.beans.property,"
+                + "javafx.beans.value,"
+                + "javafx.collections,"
+                + "javafx.concurrent,"
+                + "javafx.event,"
+                + "javafx.geometry,"
+                + "javafx.scene,"
+                + "javafx.scene.control,"
+                + "javafx.scene.image,"
+                + "javafx.scene.layout,"
+                + "javafx.scene.text,"
+                + "javafx.scene.web,"
+                + "javafx.stage,"
+                + "javafx.util,"
+                + "netscape.javascript"
+            );
+            framework = ff.newFramework(config);
+            framework.init();
+            loadClassPathBundles(framework);
+            framework.start();
+            boolean ok = false;
+            for (Bundle b : framework.getBundleContext().getBundles()) {
+                try {
+                    if (!b.getSymbolicName().equals("org.apache.aries.spifly.dynamic.bundle")) {
+                        continue;
+                    }
+                    b.start();
+                    ok = true;
+                    LOG.log(Level.INFO, "Started {0}", b.getSymbolicName());
+                } catch (BundleException ex) {
+                    LOG.log(Level.WARNING, "Cannot start bundle " + b.getSymbolicName(), ex);
+                }
+            }
+            assertTrue(ok, "Aries installed");
+            for (Bundle b : framework.getBundleContext().getBundles()) {
+                try {
+                    if (b.getSymbolicName().contains("felix.framework")) {
+                        continue;
+                    }
+                    if (b.getSymbolicName().contains("glassfish.grizzly")) {
+                        continue;
+                    }
+                    b.start();
+                    LOG.log(Level.INFO, "Started {0}", b.getSymbolicName());
+                } catch (BundleException ex) {
+                    LOG.log(Level.WARNING, "Cannot start bundle " + b.getSymbolicName(), ex);
+                }
+            }
+            return framework;
+        }
+        fail("No OSGi framework in the path");
+        return null;
+    }
+    
+    @AfterClass public static void cleanUp() throws Exception {
+        if (framework != null) framework.stop();
+        clearUpDir(dir);
+    }
+    private static void clearUpDir(File dir) {
+        if (dir.isDirectory()) {
+            for (File f : dir.listFiles()) {
+                clearUpDir(f);
+            }
+        }
+        dir.delete();
+    }
+    
+    
+
+    private static void loadClassPathBundles(Framework f) throws IOException, BundleException {
+        for (String jar : System.getProperty("java.class.path").split(File.pathSeparator)) {
+            File file = new File(jar);
+            if (!file.isFile()) {
+                LOG.info("Not loading " + file);
+                continue;
+            }
+            JarFile jf = new JarFile(file);
+            final String name = jf.getManifest().getMainAttributes().getValue("Bundle-SymbolicName");
+            jf.close();
+            if (name != null) {
+                if (name.contains("org.eclipse.osgi")) {
+                    throw new IllegalStateException("Found " + name + " !");
+                }
+                if (name.contains("felix.framework")) {
+                    continue;
+                }
+                if (name.contains("testng")) {
+                    continue;
+                }
+                final String path = "reference:" + file.toURI().toString();
+                try {
+                    Bundle b = f.getBundleContext().installBundle(path);
+                } catch (BundleException ex) {
+                    LOG.log(Level.WARNING, "Cannot install " + file, ex);
+                }
+            }
+        }
+    }
+    
+    private static Class<?> loadOSGiClass(Class<?> c) throws Exception {
+        return KnockoutFelixTCKImpl.loadOSGiClass(c.getName(), KnockoutFelixAriesIT.framework().getBundleContext());
+    }
+    
+    private static Class<?> browserClass;
+    private static Object browserContext;
+
+    @Factory public static Object[] compatibilityTests() throws Exception {
+        Class<?> tck = loadOSGiClass(KnockoutTCK.class);
+        Class<?> peer = loadOSGiClass(KnockoutFelixTCKImpl.class);
+        // initialize the TCK
+        Callable<Class[]> inst = (Callable<Class[]>) peer.newInstance();
+        
+        Class[] arr = inst.call();
+        for (int i = 0; i < arr.length; i++) {
+            if (arr[i].getClassLoader() == ClassLoader.getSystemClassLoader()) {
+                fail("Should be an OSGi class: " + arr[i]);
+            }
+        }
+        
+        URI uri = DynamicHTTP.initServer();
+
+        Method start = peer.getMethod("start", String.class, URI.class, boolean.class);
+        start.invoke(null, KnockoutFelixAriesIT.class.getName(), uri, false);
+        
+        ClassLoader l = getClassLoader();
+        List<Object> res = new ArrayList<Object>();
+        for (int i = 0; i < arr.length; i++) {
+            seekKOTests(arr[i], res);
+        }
+        return res.toArray();
+    }
+
+    private static void seekKOTests(Class<?> c, List<Object> res) throws SecurityException, ClassNotFoundException {
+        Class<? extends Annotation> koTest =
+            c.getClassLoader().loadClass(KOTest.class.getName()).
+            asSubclass(Annotation.class);
+        for (Method m : c.getMethods()) {
+            if (m.getAnnotation(koTest) != null) {
+                res.add(new KOFx(KnockoutFelixAriesIT.class, browserContext, m));
+            }
+        }
+    }
+
+    static synchronized ClassLoader getClassLoader() throws InterruptedException {
+        while (browserClass == null) {
+            KnockoutFelixAriesIT.class.wait();
+        }
+        return browserClass.getClassLoader();
+    }
+
+    public static synchronized void initialized(Class<?> browserCls, Object presenter) throws Exception {
+        browserClass = browserCls;
+        browserContext = presenter;
+        KnockoutFelixAriesIT.class.notifyAll();
+    }
+
+    public static Closeable activateInOSGi(Object presenter) throws Exception {
+        Class<?> presenterClass = loadOSGiClass(Fn.Presenter.class);
+        Class<?> fnClass = loadOSGiClass(Fn.class);
+        Method m = fnClass.getMethod("activate", presenterClass);
+        return (Closeable) m.invoke(null, presenter);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko-felix-test/src/test/java/org/netbeans/html/ko/felix/test/KnockoutFelixIT.java
----------------------------------------------------------------------
diff --git a/ko-felix-test/src/test/java/org/netbeans/html/ko/felix/test/KnockoutFelixIT.java b/ko-felix-test/src/test/java/org/netbeans/html/ko/felix/test/KnockoutFelixIT.java
new file mode 100644
index 0000000..7d085d9
--- /dev/null
+++ b/ko-felix-test/src/test/java/org/netbeans/html/ko/felix/test/KnockoutFelixIT.java
@@ -0,0 +1,253 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.ko.felix.test;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.ServiceLoader;
+import java.util.concurrent.Callable;
+import java.util.jar.JarFile;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.netbeans.html.boot.spi.Fn;
+import org.netbeans.html.json.tck.KOTest;
+import org.netbeans.html.json.tck.KnockoutTCK;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.launch.Framework;
+import org.osgi.framework.launch.FrameworkFactory;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.fail;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.Factory;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class KnockoutFelixIT {
+    private static final Logger LOG = Logger.getLogger(KnockoutFelixIT.class.getName());
+    private static Framework framework;
+    private static File dir;
+    static Framework framework() throws Exception {
+        if (framework != null) {
+            return framework;
+        }
+        for (FrameworkFactory ff : ServiceLoader.load(FrameworkFactory.class)) {
+            
+            String basedir = System.getProperty("basedir");
+            assertNotNull("basedir preperty provided", basedir);
+            File target = new File(basedir, "target");
+            dir = new File(target, "osgi");
+            dir.mkdirs();
+            
+            Map<String,String> config = new HashMap<String, String>();
+            config.put(Constants.FRAMEWORK_STORAGE, dir.getPath());
+            config.put(Constants.FRAMEWORK_STORAGE_CLEAN, "true");
+            config.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, "sun.misc,"
+                + "javafx.application,"
+                + "javafx.beans,"
+                + "javafx.beans.property,"
+                + "javafx.beans.value,"
+                + "javafx.collections,"
+                + "javafx.concurrent,"
+                + "javafx.event,"
+                + "javafx.geometry,"
+                + "javafx.scene,"
+                + "javafx.scene.control,"
+                + "javafx.scene.image,"
+                + "javafx.scene.layout,"
+                + "javafx.scene.text,"
+                + "javafx.scene.web,"
+                + "javafx.stage,"
+                + "javafx.util,"
+                + "netscape.javascript"
+            );
+            framework = ff.newFramework(config);
+            framework.init();
+            loadClassPathBundles(framework);
+            framework.start();
+            for (Bundle b : framework.getBundleContext().getBundles()) {
+                try {
+                    if (b.getSymbolicName().contains("felix.framework")) {
+                        continue;
+                    }
+                    if (b.getSymbolicName().contains("glassfish.grizzly")) {
+                        continue;
+                    }
+                    b.start();
+                    LOG.log(Level.INFO, "Started {0}", b.getSymbolicName());
+                } catch (BundleException ex) {
+                    throw new IllegalStateException("Cannot start bundle " + b.getSymbolicName(), ex);
+                }
+            }
+            return framework;
+        }
+        fail("No OSGi framework in the path");
+        return null;
+    }
+    
+    @AfterClass public static void cleanUp() throws Exception {
+        if (framework != null) framework.stop();
+        clearUpDir(dir);
+    }
+    private static void clearUpDir(File dir) {
+        if (dir.isDirectory()) {
+            for (File f : dir.listFiles()) {
+                clearUpDir(f);
+            }
+        }
+        dir.delete();
+    }
+    
+    
+
+    private static void loadClassPathBundles(Framework f) throws IOException, BundleException {
+        for (String jar : System.getProperty("java.class.path").split(File.pathSeparator)) {
+            File file = new File(jar);
+            if (!file.isFile()) {
+                LOG.info("Not loading " + file);
+                continue;
+            }
+            JarFile jf = new JarFile(file);
+            final String name = jf.getManifest().getMainAttributes().getValue("Bundle-SymbolicName");
+            jf.close();
+            if (name != null) {
+                if (name.contains("org.eclipse.osgi")) {
+                    throw new IllegalStateException("Found " + name + " !");
+                }
+                if (name.contains("felix.framework")) {
+                    continue;
+                }
+                if (name.contains("testng")) {
+                    continue;
+                }
+                if (name.equals("org.apache.aries.spifly.dynamic.bundle")) {
+                    continue;
+                }
+                final String path = "reference:" + file.toURI().toString();
+                try {
+                    Bundle b = f.getBundleContext().installBundle(path);
+                } catch (BundleException ex) {
+                    LOG.log(Level.WARNING, "Cannot install " + file, ex);
+                }
+            }
+        }
+    }
+    
+    private static Class<?> loadOSGiClass(Class<?> c) throws Exception {
+        return KnockoutFelixTCKImpl.loadOSGiClass(c.getName(), KnockoutFelixIT.framework().getBundleContext());
+    }
+    
+    private static Class<?> browserClass;
+    private static Object browserContext;
+    
+    @Factory public static Object[] compatibilityTests() throws Exception {
+        Class<?> tck = loadOSGiClass(KnockoutTCK.class);
+        Class<?> peer = loadOSGiClass(KnockoutFelixTCKImpl.class);
+        // initialize the TCK
+        Callable<Class[]> inst = (Callable<Class[]>) peer.newInstance();
+        
+        Class[] arr = inst.call();
+        for (int i = 0; i < arr.length; i++) {
+            if (arr[i].getClassLoader() == ClassLoader.getSystemClassLoader()) {
+                fail("Should be an OSGi class: " + arr[i]);
+            }
+        }
+        
+        URI uri = DynamicHTTP.initServer();
+
+        Method start = peer.getMethod("start", String.class, URI.class, boolean.class);
+        start.invoke(null, KnockoutFelixIT.class.getName(), uri, true);
+        
+        ClassLoader l = getClassLoader(null);
+        List<Object> res = new ArrayList<Object>();
+        for (int i = 0; i < arr.length; i++) {
+            seekKOTests(arr[i], res);
+        }
+        return res.toArray();
+    }
+
+    private static void seekKOTests(Class<?> c, List<Object> res) throws SecurityException, ClassNotFoundException {
+        Class<? extends Annotation> koTest =
+            c.getClassLoader().loadClass(KOTest.class.getName()).
+            asSubclass(Annotation.class);
+        for (Method m : c.getMethods()) {
+            if (m.getAnnotation(koTest) != null) {
+                res.add(new KOFx(KnockoutFelixIT.class, browserContext, m));
+            }
+        }
+    }
+
+    static synchronized ClassLoader getClassLoader(Object[] presenter) throws InterruptedException {
+        while (browserClass == null) {
+            KnockoutFelixIT.class.wait();
+        }
+        if (presenter != null) {
+            presenter[0] = browserContext;
+        }
+        return browserClass.getClassLoader();
+    }
+    
+    public static synchronized void initialized(Class<?> browserCls, Object presenter) throws Exception {
+        browserClass = browserCls;
+        browserContext = presenter;
+        KnockoutFelixIT.class.notifyAll();
+    }
+
+    public static Closeable activateInOSGi(Object presenter) throws Exception {
+        Class<?> presenterClass = loadOSGiClass(Fn.Presenter.class);
+        Class<?> fnClass = loadOSGiClass(Fn.class);
+        Method m = fnClass.getMethod("activate", presenterClass);
+        return (Closeable) m.invoke(null, presenter);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko-felix-test/src/test/resources/org/netbeans/html/ko/felix/test/test.html
----------------------------------------------------------------------
diff --git a/ko-felix-test/src/test/resources/org/netbeans/html/ko/felix/test/test.html b/ko-felix-test/src/test/resources/org/netbeans/html/ko/felix/test/test.html
new file mode 100644
index 0000000..6edd632
--- /dev/null
+++ b/ko-felix-test/src/test/resources/org/netbeans/html/ko/felix/test/test.html
@@ -0,0 +1,56 @@
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>Knockout.fx Execution Harness</title>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <meta name="viewport" content="width=device-width">
+    </head>
+    <body>
+        <h1>Knockout.fx in Felix Execution Harness</h1>
+    </body>
+    <script></script>
+</html>


[15/24] incubator-netbeans-html4j git commit: [INFRA-15006] Initial donation of HTML/Java NetBeans API. Equivalent to the content of html4j-donation-review.zip donated as part of ApacheNetBeansDonation1.zip with SHA256 being 7f2ca0f61953a190613c9a0fbcc1b

Posted by jt...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/geo/src/test/java/org/netbeans/html/geo/impl/Compile.java
----------------------------------------------------------------------
diff --git a/geo/src/test/java/org/netbeans/html/geo/impl/Compile.java b/geo/src/test/java/org/netbeans/html/geo/impl/Compile.java
new file mode 100644
index 0000000..69c0273
--- /dev/null
+++ b/geo/src/test/java/org/netbeans/html/geo/impl/Compile.java
@@ -0,0 +1,286 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.geo.impl;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.tools.Diagnostic;
+import javax.tools.DiagnosticListener;
+import javax.tools.FileObject;
+import javax.tools.ForwardingJavaFileManager;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+import javax.tools.JavaFileObject.Kind;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.StandardLocation;
+import javax.tools.ToolProvider;
+import static org.testng.Assert.*;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+final class Compile implements DiagnosticListener<JavaFileObject> {
+    private final List<Diagnostic<? extends JavaFileObject>> errors = 
+            new ArrayList<Diagnostic<? extends JavaFileObject>>();
+    private final Map<String, byte[]> classes;
+    private final String pkg;
+    private final String cls;
+    private final String html;
+    private final String sourceLevel;
+
+    private Compile(String html, String code, String sl) throws IOException {
+        this.pkg = findPkg(code);
+        this.cls = findCls(code);
+        this.html = html;
+        this.sourceLevel = sl;
+        classes = compile(html, code);
+    }
+
+    /** Performs compilation of given HTML page and associated Java code
+     */
+    public static Compile create(String html, String code) throws IOException {
+        return create(html, code, "1.7");
+    }
+    static Compile create(String html, String code, String sourceLevel) throws IOException {
+        return new Compile(html, code, sourceLevel);
+    }
+    
+    /** Checks for given class among compiled resources */
+    public byte[] get(String res) {
+        return classes.get(res);
+    }
+    
+    /** Obtains errors created during compilation.
+     */
+    public List<Diagnostic<? extends JavaFileObject>> getErrors() {
+        List<Diagnostic<? extends JavaFileObject>> err;
+        err = new ArrayList<Diagnostic<? extends JavaFileObject>>();
+        for (Diagnostic<? extends JavaFileObject> diagnostic : errors) {
+            if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
+                err.add(diagnostic);
+            }
+        }
+        return err;
+    }
+    
+    private Map<String, byte[]> compile(final String html, final String code) throws IOException {
+        StandardJavaFileManager sjfm = ToolProvider.getSystemJavaCompiler().getStandardFileManager(this, null, null);
+
+        final Map<String, ByteArrayOutputStream> class2BAOS;
+        class2BAOS = new HashMap<String, ByteArrayOutputStream>();
+
+        JavaFileObject file = new SimpleJavaFileObject(URI.create("mem://mem"), Kind.SOURCE) {
+            @Override
+            public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+                return code;
+            }
+        };
+        final JavaFileObject htmlFile = new SimpleJavaFileObject(URI.create("mem://mem2"), Kind.OTHER) {
+            @Override
+            public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+                return html;
+            }
+
+            @Override
+            public InputStream openInputStream() throws IOException {
+                return new ByteArrayInputStream(html.getBytes());
+            }
+        };
+        
+        final URI scratch;
+        try {
+            scratch = new URI("mem://mem3");
+        } catch (URISyntaxException ex) {
+            throw new IOException(ex);
+        }
+        
+        JavaFileManager jfm = new ForwardingJavaFileManager<JavaFileManager>(sjfm) {
+            @Override
+            public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) throws IOException {
+                if (kind  == Kind.CLASS) {
+                    final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+
+                    class2BAOS.put(className.replace('.', '/') + ".class", buffer);
+                    return new SimpleJavaFileObject(sibling.toUri(), kind) {
+                        @Override
+                        public OutputStream openOutputStream() throws IOException {
+                            return buffer;
+                        }
+                    };
+                }
+                
+                if (kind == Kind.SOURCE) {
+                    final String n = className.replace('.', '/') + ".java";
+                    final URI un;
+                    try {
+                        un = new URI("mem://" + n);
+                    } catch (URISyntaxException ex) {
+                        throw new IOException(ex);
+                    }
+                    return new VirtFO(un/*sibling.toUri()*/, kind, n);
+                }
+                
+                throw new IllegalStateException();
+            }
+
+            @Override
+            public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException {
+                if (location == StandardLocation.SOURCE_PATH) {
+                    if (packageName.equals(pkg)) {
+                        return htmlFile;
+                    }
+                }
+                
+                return null;
+            }
+
+            @Override
+            public boolean isSameFile(FileObject a, FileObject b) {
+                if (a instanceof VirtFO && b instanceof VirtFO) {
+                    return ((VirtFO)a).getName().equals(((VirtFO)b).getName());
+                }
+                
+                return super.isSameFile(a, b);
+            }
+
+            class VirtFO extends SimpleJavaFileObject {
+
+                private final String n;
+
+                public VirtFO(URI uri, Kind kind, String n) {
+                    super(uri, kind);
+                    this.n = n;
+                }
+                private final ByteArrayOutputStream data = new ByteArrayOutputStream();
+
+                @Override
+                public OutputStream openOutputStream() throws IOException {
+                    return data;
+                }
+
+                @Override
+                public String getName() {
+                    return n;
+                }
+
+                @Override
+                public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+                    data.close();
+                    return new String(data.toByteArray());
+                }
+            }
+        };
+
+        ToolProvider.getSystemJavaCompiler().getTask(null, jfm, this, /*XXX:*/Arrays.asList("-source", sourceLevel, "-target", "1.7"), null, Arrays.asList(file)).call();
+
+        Map<String, byte[]> result = new HashMap<String, byte[]>();
+
+        for (Map.Entry<String, ByteArrayOutputStream> e : class2BAOS.entrySet()) {
+            result.put(e.getKey(), e.getValue().toByteArray());
+        }
+
+        return result;
+    }
+
+
+    @Override
+    public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
+        errors.add(diagnostic);
+    }
+    private static String findPkg(String java) throws IOException {
+        Pattern p = Pattern.compile("package\\p{javaWhitespace}*([\\p{Alnum}\\.]+)\\p{javaWhitespace}*;", Pattern.MULTILINE);
+        Matcher m = p.matcher(java);
+        if (!m.find()) {
+            throw new IOException("Can't find package declaration in the java file");
+        }
+        String pkg = m.group(1);
+        return pkg;
+    }
+    private static String findCls(String java) throws IOException {
+        Pattern p = Pattern.compile("class\\p{javaWhitespace}*([\\p{Alnum}\\.]+)\\p{javaWhitespace}", Pattern.MULTILINE);
+        Matcher m = p.matcher(java);
+        if (!m.find()) {
+            throw new IOException("Can't find package declaration in the java file");
+        }
+        String cls = m.group(1);
+        return cls;
+    }
+
+    String getHtml() {
+        String fqn = "'" + pkg + '.' + cls + "'";
+        return html.replace("'${fqn}'", fqn);
+    }
+
+    void assertErrors() {
+        assertFalse(getErrors().isEmpty(), "There are supposed to be some errors");
+    }
+
+    void assertError(String expMsg) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("Can't find ").append(expMsg).append(" among:");
+        for (Diagnostic<? extends JavaFileObject> e : errors) {
+            String msg = e.getMessage(Locale.US);
+            if (msg.contains(expMsg)) {
+                return;
+            }
+            sb.append("\n");
+            sb.append(msg);
+        }
+        fail(sb.toString());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/geo/src/test/java/org/netbeans/html/geo/impl/GeoProcessorTest.java
----------------------------------------------------------------------
diff --git a/geo/src/test/java/org/netbeans/html/geo/impl/GeoProcessorTest.java b/geo/src/test/java/org/netbeans/html/geo/impl/GeoProcessorTest.java
new file mode 100644
index 0000000..8ef0846
--- /dev/null
+++ b/geo/src/test/java/org/netbeans/html/geo/impl/GeoProcessorTest.java
@@ -0,0 +1,114 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.geo.impl;
+
+import java.io.IOException;
+import org.testng.annotations.Test;
+
+/** Test whether the annotation processor detects errors correctly.
+ *
+ * @author Jaroslav Tulach
+ */
+public class GeoProcessorTest {
+    
+    public GeoProcessorTest() {
+    }
+
+    @Test public void onLocationMethodHasToTakePositionParameter() throws IOException {
+        Compile res = Compile.create("", "package x;\n"
+            + "class UseOnLocation {\n"
+            + "  @net.java.html.geo.OnLocation\n"
+            + "  public static void cantCallMe() {}\n"
+            + "}\n"
+        );
+        res.assertErrors();
+        res.assertError("first argument must be net.java.html.geo.Position");
+    }
+    
+    @Test public void onLocationMethodCannotBePrivate() throws IOException {
+        Compile res = Compile.create("", "package x;\n"
+            + "class UseOnLocation {\n"
+            + "  @net.java.html.geo.OnLocation\n"
+            + "  private static void cantCallMe(net.java.html.geo.Position p) {}\n"
+            + "}\n"
+        );
+        res.assertErrors();
+        res.assertError("cannot be private");
+    }
+    
+    @Test public void onErrorHasToExist() throws IOException {
+        Compile res = Compile.create("", "package x;\n"
+            + "class UseOnLocation {\n"
+            + "  @net.java.html.geo.OnLocation(onError=\"doesNotExist\")\n"
+            + "  static void cantCallMe(net.java.html.geo.Position p) {}\n"
+            + "}\n"
+        );
+        res.assertErrors();
+        res.assertError("not find doesNotExist");
+    }
+
+    @Test public void onErrorWouldHaveToBeStatic() throws IOException {
+        Compile res = Compile.create("", "package x;\n"
+            + "class UseOnLocation {\n"
+            + "  @net.java.html.geo.OnLocation(onError=\"notStatic\")\n"
+            + "  static void cantCallMe(net.java.html.geo.Position p) {}\n"
+            + "  void notStatic(Exception e) {}\n"
+            + "}\n"
+        );
+        res.assertErrors();
+        res.assertError("have to be static");
+    }
+
+    @Test public void onErrorMustAcceptExceptionArgument() throws IOException {
+        Compile res = Compile.create("", "package x;\n"
+            + "class UseOnLocation {\n"
+            + "  @net.java.html.geo.OnLocation(onError=\"notStatic\")\n"
+            + "  static void cantCallMe(net.java.html.geo.Position p) {}\n"
+            + "  static void notStatic(java.io.IOException e) {}\n"
+            + "}\n"
+        );
+        res.assertErrors();
+        res.assertError("Error method first argument needs to be Exception");
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/geo/src/test/java/org/netbeans/html/geo/impl/JsGLProviderTest.java
----------------------------------------------------------------------
diff --git a/geo/src/test/java/org/netbeans/html/geo/impl/JsGLProviderTest.java b/geo/src/test/java/org/netbeans/html/geo/impl/JsGLProviderTest.java
new file mode 100644
index 0000000..d2a73a4
--- /dev/null
+++ b/geo/src/test/java/org/netbeans/html/geo/impl/JsGLProviderTest.java
@@ -0,0 +1,76 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.geo.impl;
+
+import net.java.html.geo.Position;
+import static org.testng.Assert.*;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach <jt...@netbeans.org>
+ */
+public class JsGLProviderTest extends Position.Handle {
+    public JsGLProviderTest() {
+        super(true);
+    }
+
+    @Test public void checkWhetherWeCanInstantiate() {
+        assertNotNull(new JsGLProvider());
+    }
+
+    @Test public void canCallIsSupported() {
+        assertFalse(isSupported(), "Well, it is not, as we are not in a browser context");
+    }
+
+    @Override
+    protected void onLocation(Position p) throws Throwable {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected void onError(Exception ex) throws Throwable {
+        throw new UnsupportedOperationException();
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/geo/src/test/java/org/netbeans/html/geo/spi/CoordImplTest.java
----------------------------------------------------------------------
diff --git a/geo/src/test/java/org/netbeans/html/geo/spi/CoordImplTest.java b/geo/src/test/java/org/netbeans/html/geo/spi/CoordImplTest.java
new file mode 100644
index 0000000..01f1b31
--- /dev/null
+++ b/geo/src/test/java/org/netbeans/html/geo/spi/CoordImplTest.java
@@ -0,0 +1,106 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.geo.spi;
+
+import static org.testng.Assert.*;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class CoordImplTest extends GLProvider<Double, Object> {
+    
+    public CoordImplTest() {
+    }
+    @Test public void testGetLatitude() {
+        CoordImpl<Double> c = new CoordImpl<Double>(50.5, this);
+        assertEquals(c.getLatitude(), 50.5, 0.1, "Latitude returned as provided");
+    }
+
+    @Override
+    protected Object start(Query c) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected void stop(Object watch) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected double latitude(Double coords) {
+        return coords;
+    }
+
+    @Override
+    protected double longitude(Double coords) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected double accuracy(Double coords) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected Double altitude(Double coords) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected Double altitudeAccuracy(Double coords) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected Double heading(Double coords) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected Double speed(Double coords) {
+        throw new UnsupportedOperationException();
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/html4j-maven-plugin/pom.xml
----------------------------------------------------------------------
diff --git a/html4j-maven-plugin/pom.xml b/html4j-maven-plugin/pom.xml
new file mode 100644
index 0000000..56cf9b6
--- /dev/null
+++ b/html4j-maven-plugin/pom.xml
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2016 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<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">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.netbeans.html</groupId>
+    <artifactId>pom</artifactId>
+    <version>2.0-SNAPSHOT</version>
+  </parent>
+  <packaging>maven-plugin</packaging>
+  <groupId>org.netbeans.html</groupId>
+  <artifactId>html4j-maven-plugin</artifactId>
+  <version>2.0-SNAPSHOT</version>
+  <name>Html for Java Maven Plugin</name>
+  <url>http://maven.apache.org</url>
+  <description>Maven plugin to post process the classes with @JavaScriptBody annotations</description>
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+  </properties>
+  <build>
+      <plugins>
+          <plugin>
+              <groupId>org.apache.maven.plugins</groupId>
+              <artifactId>maven-plugin-plugin</artifactId>
+              <version>3.1</version>
+              <configuration>
+                  <extractors>
+                      <extractor>java-annotations</extractor>
+                  </extractors>
+                  <skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>
+              </configuration>
+              <executions>
+                  <execution>
+                      <id>mojo-descriptor</id>
+                      <phase>process-classes</phase>
+                      <goals>
+                          <goal>descriptor</goal>
+                      </goals>
+                  </execution>
+              </executions>
+          </plugin>
+      </plugins>
+  </build>
+      
+  <dependencies>
+      <dependency>
+          <groupId>org.apache.maven</groupId>
+          <artifactId>maven-plugin-api</artifactId>
+          <version>3.0.4</version>
+          <type>jar</type>
+      </dependency>
+      <dependency>
+          <groupId>org.apache.maven.plugin-tools</groupId>
+          <artifactId>maven-plugin-annotations</artifactId>
+          <version>3.0</version>
+          <type>jar</type>
+      </dependency>
+      <dependency>
+          <groupId>org.apache.maven</groupId>
+          <artifactId>maven-core</artifactId>
+          <version>3.0.2</version>
+          <type>jar</type>
+      </dependency>
+      <dependency>
+          <groupId>org.ow2.asm</groupId> 
+          <artifactId>asm</artifactId>
+          <type>jar</type>
+      </dependency>
+      <dependency>
+          <groupId>org.testng</groupId>
+          <artifactId>testng</artifactId>
+          <scope>test</scope>
+          <type>jar</type>
+      </dependency>
+  </dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/html4j-maven-plugin/src/main/java/org/netbeans/html/mojo/ProcessJsAnnotationsMojo.java
----------------------------------------------------------------------
diff --git a/html4j-maven-plugin/src/main/java/org/netbeans/html/mojo/ProcessJsAnnotationsMojo.java b/html4j-maven-plugin/src/main/java/org/netbeans/html/mojo/ProcessJsAnnotationsMojo.java
new file mode 100644
index 0000000..4755ee2
--- /dev/null
+++ b/html4j-maven-plugin/src/main/java/org/netbeans/html/mojo/ProcessJsAnnotationsMojo.java
@@ -0,0 +1,223 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.mojo;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugins.annotations.Component;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.plugins.annotations.ResolutionScope;
+import org.apache.maven.project.MavenProject;
+import org.objectweb.asm.ClassReader;
+
+@Mojo(
+    name="process-js-annotations",
+    requiresDependencyResolution = ResolutionScope.COMPILE,
+    defaultPhase= LifecyclePhase.PROCESS_CLASSES
+)
+public final class ProcessJsAnnotationsMojo extends AbstractMojo {
+    @Component
+    private MavenProject prj;
+    
+    @Parameter(defaultValue = "${project.build.directory}/classes")
+    private File classes;
+    
+    public ProcessJsAnnotationsMojo() {
+    }
+
+    @Override
+    public void execute() throws MojoExecutionException, MojoFailureException {
+        LinkedList<URL> arr = new LinkedList<URL>();
+        boolean foundAsm = false;
+        for (Artifact a : prj.getArtifacts()) {
+            final File f = a.getFile();
+            if (f != null) {
+                if (a.getArtifactId().equals("asm")) {
+                    foundAsm = true;
+                }
+                try {
+                    arr.add(f.toURI().toURL());
+                } catch (MalformedURLException ex) {
+                    throw new IllegalStateException(ex);
+                }
+            }
+        }
+        if (!foundAsm) {
+            URL loc = ClassReader.class.getProtectionDomain().getCodeSource().getLocation();
+            arr.addFirst(loc);
+        }
+        try {
+            arr.add(classes.toURI().toURL());
+            URLClassLoader l = new URLClassLoader(arr.toArray(new URL[arr.size()]));
+            File master = new File(new File(classes, "META-INF"), "net.java.html.js.classes");
+            processClasses(l, master, classes);
+        } catch (IOException ex) {
+            throw new MojoExecutionException("Problem converting JavaScriptXXX annotations", ex);
+        }
+    }
+    
+    private void processClasses(ClassLoader l, File master, File f) throws IOException, MojoExecutionException {
+        if (!f.exists()) {
+            return;
+        }
+        if (f.isDirectory()) {
+            boolean classes = new File(f, "net.java.html.js.classes").exists();
+            File[] arr = f.listFiles();
+            if (arr != null) {
+                for (File file : arr) {
+                    if (classes || file.isDirectory()) {
+                        processClasses(l, master, file);
+                    }
+                }
+            }
+            return;
+        }
+        
+        if (!f.getName().endsWith(".class")) {
+            return;
+        }
+        
+        byte[] arr = new byte[(int)f.length()];
+        FileInputStream is = new FileInputStream(f);
+        try {
+            readArr(arr, is);
+        } finally {
+            is.close();
+        }
+
+        byte[] newArr = null;
+        try {
+            Class<?> fnUtils = l.loadClass("org.netbeans.html.boot.impl.FnUtils");
+            Method transform = fnUtils.getMethod("transform", byte[].class, ClassLoader.class);
+            
+            newArr = (byte[]) transform.invoke(null, arr, l);
+            if (newArr == null || newArr == arr) {
+                return;
+            }
+            filterClass(new File(f.getParentFile(), "net.java.html.js.classes"), f.getName());
+            filterClass(master, f.getName());
+        } catch (Exception ex) {
+            throw new MojoExecutionException("Can't process " + f, ex);
+        }
+        getLog().info("Processing " + f);
+        writeArr(f, newArr);        
+    }
+
+    private void writeArr(File f, byte[] newArr) throws IOException, FileNotFoundException {
+        FileOutputStream os = new FileOutputStream(f);
+        try {
+            os.write(newArr);
+        } finally {
+            os.close();
+        }
+    }
+
+    private static void readArr(byte[] arr, InputStream is) throws IOException {
+        int off = 0;
+        while (off< arr.length) {
+            int read = is.read(arr, off, arr.length - off);
+            if (read == -1) {
+                break;
+            }
+            off += read;
+        }
+    }
+    
+    private static void filterClass(File f, String className) throws IOException {
+        if (!f.exists()) {
+            return;
+        }
+        if (className.endsWith(".class")) {
+            className = className.substring(0, className.length() - 6);
+        }
+        
+        BufferedReader r = new BufferedReader(new FileReader(f));
+        List<String> arr = new ArrayList<String>();
+        boolean modified = false;
+        for (;;) {
+            String line = r.readLine();
+            if (line == null) {
+                break;
+            }
+            if (line.endsWith(className)) {
+                modified = true;
+                continue;
+            }
+            arr.add(line);
+        }
+        r.close();
+        
+        if (modified) {
+            if (arr.isEmpty()) {
+                f.delete();
+            } else {
+                FileWriter w = new FileWriter(f);
+                for (String l : arr) {
+                    w.write(l);
+                    w.write("\n");
+                }
+                w.close();
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json-tck/pom.xml
----------------------------------------------------------------------
diff --git a/json-tck/pom.xml b/json-tck/pom.xml
new file mode 100644
index 0000000..161b19d
--- /dev/null
+++ b/json-tck/pom.xml
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2016 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<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">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.netbeans.html</groupId>
+    <artifactId>pom</artifactId>
+    <version>2.0-SNAPSHOT</version>
+  </parent>
+  <groupId>org.netbeans.html</groupId>
+  <artifactId>net.java.html.json.tck</artifactId>
+  <version>2.0-SNAPSHOT</version>
+  <packaging>bundle</packaging>
+  <name>HTML for Java TCK</name>
+  <url>http://maven.apache.org</url>
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <publicPackages>org.netbeans.html.json.tck</publicPackages>
+  </properties>
+  <build>
+      <plugins>
+          <plugin>
+              <groupId>org.apache.felix</groupId>
+              <artifactId>maven-bundle-plugin</artifactId>
+          </plugin>
+          <plugin>
+              <groupId>org.netbeans.html</groupId>
+              <artifactId>html4j-maven-plugin</artifactId>
+          </plugin>
+          <plugin>
+              <groupId>org.apache.maven.plugins</groupId>
+              <artifactId>maven-javadoc-plugin</artifactId>
+              <configuration>
+                  <subpackages>org.netbeans.html.json.tck,org.netbeans.html.json.spi</subpackages>
+                  <skip>false</skip>
+                  <includeDependencySources>true</includeDependencySources>
+              </configuration>
+          </plugin>
+      </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>org.netbeans.html</groupId>
+      <artifactId>net.java.html.json</artifactId>
+      <version>2.0-SNAPSHOT</version>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+      <groupId>org.testng</groupId>
+      <artifactId>testng</artifactId>
+      <scope>compile</scope>
+      <type>jar</type>
+      <exclusions>
+        <exclusion>
+          <artifactId>bsh</artifactId>
+          <groupId>org.beanshell</groupId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>org.netbeans.api</groupId>
+      <artifactId>org-openide-util</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>net.java.html.boot</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+    <description>Test Compatibility Kit for anyone who wants to consume the net.java.html.json APIs and
+render their objects using own technology (e.g. own browser, MVVC, etc.).</description>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json-tck/src/main/java/net/java/html/js/tests/Bodies.java
----------------------------------------------------------------------
diff --git a/json-tck/src/main/java/net/java/html/js/tests/Bodies.java b/json-tck/src/main/java/net/java/html/js/tests/Bodies.java
new file mode 100644
index 0000000..029a3a2
--- /dev/null
+++ b/json-tck/src/main/java/net/java/html/js/tests/Bodies.java
@@ -0,0 +1,254 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.js.tests;
+
+import java.util.concurrent.Callable;
+import net.java.html.js.JavaScriptBody;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+final class Bodies {
+    @JavaScriptBody(args = { "a", "b" }, body = "return a + b;")
+    public static native int sum(int a, int b);
+
+    @JavaScriptBody(args = { "a", "b" }, javacall = true, body = 
+        "return @net.java.html.js.tests.Bodies::sum(II)(a, b);"
+    )
+    public static native int sumJS(int a, int b);
+    
+    @JavaScriptBody(args = {"r"}, javacall = true, body = "r.@java.lang.Runnable::run()();")
+    static native void callback(Runnable r);
+
+    @JavaScriptBody(args = {"r"}, wait4js = false, keepAlive = false, javacall = true, body = "r.@java.lang.Runnable::run()();")
+    static native void asyncCallback(Runnable r);
+    
+    @JavaScriptBody(args = {"c", "v"}, javacall = true, body = "var arr = c.@java.util.concurrent.Callable::call()(); arr.push(v); return arr;")
+    static native Object callbackAndPush(Callable<String[]> c, String v);
+    
+    @JavaScriptBody(args = { "v" }, body = "return v;")
+    public static native Object id(Object v);
+    
+    @JavaScriptBody(args = { "v" }, body = "return { 'x' : v };")
+    public static native Object instance(int v);
+    
+    @JavaScriptBody(args = "o", body = "o.x++;")
+    public static native void incrementX(Object o);
+
+    @JavaScriptBody(args = "o", wait4js = true, body = "o.x++;")
+    static native void incrementXAsync(Object o);
+
+    @JavaScriptBody(args = "o", body = "return o.x;")
+    public static native int readIntX(Object o);
+    
+    @JavaScriptBody(args = "o", body = "return o.x;")
+    public static native Object readX(Object o);
+    
+    @JavaScriptBody(args = { "o", "x" }, keepAlive = false, body = "o.x = x;")
+    public static native Object setX(Object o, Object x);
+
+    @JavaScriptBody(args = { "c", "a", "b" }, keepAlive = false, javacall = true, body = 
+        "return c.@net.java.html.js.tests.Sum::sum(II)(a, b);"
+    )
+    public static native int sumIndirect(Sum c, int a, int b);
+    
+    @JavaScriptBody(args = { "arr", "index" }, body = "return arr[index];")
+    public static native Object select(Object[] arr, int index);
+
+    @JavaScriptBody(args = { "arr" }, body = "return arr.length;")
+    public static native int length(Object[] arr);
+    
+    @JavaScriptBody(args = { "o", "vo" }, body = "if (vo) o = o.valueOf(); return typeof o;")
+    public static native String typeof(Object o, boolean useValueOf);
+
+    @JavaScriptBody(args = { "b" }, body = "return typeof b;")
+    public static native String typeof(boolean b);
+
+    @JavaScriptBody(args = { "o" }, body = "return Array.isArray(o);")
+    public static native boolean isArray(Object o);
+
+    @JavaScriptBody(args = { "arr", "i", "value" }, body = "arr[i] = value; return arr[i];")
+    public static native String modify(String[] arr, int i, String value);
+    
+    @JavaScriptBody(args = {}, body = "return true;")
+    public static native boolean truth();
+    
+    @JavaScriptBody(args = { "s" }, javacall = true, body = 
+        "return s.@net.java.html.js.tests.Sum::sum([Ljava/lang/Object;)([1, 2, 3]);"
+    )
+    public static native int sumArr(Sum s);
+    
+    @JavaScriptBody(args = {}, javacall = true, body = 
+        "return @net.java.html.js.tests.Bodies::fourtyTwo()();"
+    )
+    public static native int staticCallback();
+
+    @JavaScriptBody(args = {}, javacall = true, body = 
+        "return function() { return @net.java.html.js.tests.Bodies::fourtyTwo()(); }"
+    )
+    public static native Object delayCallback();
+    
+    @JavaScriptBody(args = { "fn" }, body = "return fn();")
+    public static native Object invokeFn(Object fn);
+    
+    static int fourtyTwo() {
+        return 42;
+    }
+    
+    @JavaScriptBody(args = { "arr" }, body = 
+        "var sum = 0;\n" +
+        "for (var i = 0; i < arr.length; i++) {\n" +
+        "  sum += arr[i];\n" +
+        "}\n" +
+        "return sum;\n"
+    )
+    public static native double sumVector(double[] arr);
+    
+    @JavaScriptBody(args = { "arr" }, body = 
+        "var sum = 0;\n" +
+        "for (var i = 0; i < arr.length; i++) {\n" +
+        "  for (var j = 0; j < arr[i].length; j++) {\n" +
+        "    sum += arr[i][j];\n" +
+        "  }\n" +
+        "}\n" +
+        "return sum;\n"
+    )
+    public static native double sumMatrix(double[][] arr);
+
+    static void incCounter(int howMuch, final Object js) {
+        for (int i = 0; i < howMuch; i++) {
+            asyncCallback(new Runnable() {
+                @Override
+                public void run() {
+                    incrementXAsync(js);
+                }
+            });
+        }
+    }
+    
+    @JavaScriptBody(args = {}, javacall = true, body = 
+        "var v = { x : 0 };\n" +
+        "@net.java.html.js.tests.Bodies::incCounter(ILjava/lang/Object;)(42, v);\n" +
+        "return v.x;\n"
+    )
+    static native int incAsync();
+    
+    @JavaScriptBody(args = { "arr" }, body = 
+        "var ret = [];\n" +
+        "for (var i in arr) {\n" +
+        "  ret.push(arr[i]);\n" +
+        "}\n" +
+        "return ret;\n"
+    )
+    static native Object[] forIn(Object[] in);
+
+    @JavaScriptBody(args = { "max" }, body = 
+        "var arr = [];\n"
+      + "for (var i = 0; i < max; i++) {\n"
+      + "  arr.push(i);\n"
+      + "}\n"
+      + "return arr.length;"
+    )
+    static native int gc(double max);
+
+    @JavaScriptBody(args = {}, body = ""
+        + "var o = {};\n"
+        + "return o.x;\n"
+    )
+    static native Object unknown();
+
+    @JavaScriptBody(args = {}, body = ""
+        + "return new Array(2);\n"
+    )
+    static native Object[] unknownArray();
+
+    @JavaScriptBody(args = { "sum" }, javacall = true, body = ""
+        + "var arr = [];\n"
+        + "arr[1] = null;\n"
+        + "arr[2] = 1;\n"
+        + "return sum.@net.java.html.js.tests.Sum::sumNonNull([Ljava/lang/Object;)(arr);\n"
+    )
+    static native int sumNonNull(Sum sum);
+
+    @JavaScriptBody(args = { "sum", "p" }, javacall = true, body = ""
+        + "var obj = {};\n"
+        + "obj.x = 1;\n"
+        + "return sum.@net.java.html.js.tests.Sum::checkNonNull(Ljava/lang/Object;)(obj[p]);\n"
+    )
+    static native boolean nonNull(Sum sum, String p);
+
+    @JavaScriptBody(args = {}, javacall = true, body = 
+        "return @net.java.html.js.tests.Bodies::problematicString()();"
+    )
+    public static native String problematicCallback();
+
+    @JavaScriptBody(args = { "sum" }, javacall = true, body = 
+        "return sum.@net.java.html.js.tests.Sum::all(ZBSIJFDCLjava/lang/String;)(false, 1, 2, 3, 5, 6, 7, 32, 'TheEND');\n"
+    )
+    static native String primitiveTypes(Sum sum);
+
+    @JavaScriptBody(args = { "call" }, javacall = true, body = ""
+        + "var b = call.@java.util.concurrent.Callable::call()();\n"
+        + "return b ? 'yes' : 'no';\n"
+    )
+    static native String yesNo(Callable<Boolean> call);
+
+    @JavaScriptBody(args = {"arr", "val"}, body = "return arr[0] === val;")
+    public static native boolean isInArray(Object[] arr, Object val);
+    
+    @JavaScriptBody(args = {}, body = "return globalString;")
+    static native String readGlobalString();
+
+    @JavaScriptBody(args = {}, body = "return global2String;")
+    static native String readGlobal2String();
+    
+    static String problematicString() {
+        return "{\n" +
+"    MyViewModel: {\n" +
+"//      ViewModel: JavaViewModel,\n" +
+"\n" +
+"    }          \n" +
+"}";
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json-tck/src/main/java/net/java/html/js/tests/Factorial.java
----------------------------------------------------------------------
diff --git a/json-tck/src/main/java/net/java/html/js/tests/Factorial.java b/json-tck/src/main/java/net/java/html/js/tests/Factorial.java
new file mode 100644
index 0000000..d5d631a
--- /dev/null
+++ b/json-tck/src/main/java/net/java/html/js/tests/Factorial.java
@@ -0,0 +1,62 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.js.tests;
+
+import net.java.html.js.JavaScriptBody;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public final class Factorial {
+    int minusOne(int i) {
+        return i - 1;
+    }
+
+    @JavaScriptBody(args = { "i" }, javacall = true,body = 
+        "if (i <= 1) return 1;\n"
+      + "var im1 = this.@net.java.html.js.tests.Factorial::minusOne(I)(i);\n"
+      + "return this.@net.java.html.js.tests.Factorial::factorial(I)(im1) * i;"
+    )
+    native int factorial(int n);
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json-tck/src/main/java/net/java/html/js/tests/GCBodyTest.java
----------------------------------------------------------------------
diff --git a/json-tck/src/main/java/net/java/html/js/tests/GCBodyTest.java b/json-tck/src/main/java/net/java/html/js/tests/GCBodyTest.java
new file mode 100644
index 0000000..46d0775
--- /dev/null
+++ b/json-tck/src/main/java/net/java/html/js/tests/GCBodyTest.java
@@ -0,0 +1,178 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.js.tests;
+
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import org.netbeans.html.json.tck.KOTest;
+import static net.java.html.js.tests.JavaScriptBodyTest.*;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class GCBodyTest {
+    Reference<?> ref;
+    int[] arr;
+    
+    @KOTest public void callbackInterfaceCanDisappear() throws InterruptedException {
+        if (ref != null) {
+            assertGC(ref, "Can disappear!");
+            return;
+        }
+        Sum s = new Sum();
+        int res = Bodies.sumIndirect(s, 22, 20);
+        assertEquals(res, 42, "Expecting 42");
+        Reference<?> ref = new WeakReference<Object>(s);
+        s = null;
+        assertGC(ref, "Can disappear!");
+    }
+    
+    private Object assignInst() {
+        Object obj = Bodies.instance(0);
+        Object s = new EmptyInstance();
+        Bodies.setX(obj, s);
+        assertEquals(s, Bodies.readX(obj));
+        ref = new WeakReference<Object>(s);
+        return obj;
+}
+    
+    @KOTest public void holdObjectAndReleaseObject() throws InterruptedException {
+        if (ref != null) {
+            assertGC(ref, "Can disappear!");
+            return;
+        }
+        
+        Object obj = assignInst();
+        assertNotNull(ref, "Reference assigned");
+        
+        assertGC(ref, "Can disappear as it is keepAlive false!");
+        assertNotNull(obj, "Object is still present");
+    }
+
+    @KOTest public void strongReceiverBehavior() {
+        Object v = new EmptyInstance();
+        Receiver r = new Receiver(v);
+        r.apply();
+        assertEquals(v, r.value, "Value is as expected");
+    }
+    
+    @KOTest public void gcReceiverBehavior() throws InterruptedException {
+        Receiver r = new Receiver(new EmptyInstance());
+        assertGC(r.ref, "The empty instance can be GCed even when referenced from JS");
+        r.apply();
+        assertEquals(r.value, null, "Setter called with null value");
+    }
+
+    private static Reference<?> sendRunnable(final int[] arr) {
+        Runnable r = new Runnable() {
+            @Override
+            public void run() {
+                arr[0]++;
+            }
+        };
+        Bodies.asyncCallback(r);
+        return new WeakReference<Object>(r);
+    }
+    
+    private static class EmptyInstance {
+    }
+    
+    @KOTest public void parametersNeedToRemainInAsyncMode() throws InterruptedException {
+        if (ref != null) {
+            if (arr[0] != 1) {
+                throw new InterruptedException();
+            }
+            assertGC(ref, "Now the runnable can disappear");
+            return;
+        }
+        arr = new int[] { 0 };
+        ref = sendRunnable(arr);
+        if (arr[0] == 1) {
+            return;
+        }
+        assertNotGC(ref, false, "The runnable should not be GCed");
+        throw new InterruptedException();
+    }
+    
+    private static void assertGC(Reference<?> ref, String msg) throws InterruptedException {
+        for (int i = 0; i < 100; i++) {
+            if (isGone(ref)) return;
+            long then = System.currentTimeMillis();
+            int size = Bodies.gc(Math.pow(2.0, i));
+            long took = System.currentTimeMillis() - then;
+            if (took > 3000) {
+                throw new InterruptedException(msg + " - giving up after " + took + " ms at size of " + size);
+            }
+            
+            try {
+                System.gc();
+                System.runFinalization();
+            } catch (Error err) {
+                err.printStackTrace();
+            }
+        }
+        throw new InterruptedException(msg);
+    }
+
+    private static boolean isGone(Reference<?> ref) {
+        return ref.get() == null;
+    }
+    
+    private static void assertNotGC(Reference<?> ref, boolean clearJS, String msg) throws InterruptedException {
+        for (int i = 0; i < 10; i++) {
+            if (ref.get() == null) {
+                throw new IllegalStateException(msg);
+            }
+            if (clearJS) {
+                Bodies.gc(Math.pow(2.0, i));
+            }
+            try {
+                System.gc();
+                System.runFinalization();
+            } catch (Error err) {
+                err.printStackTrace();
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json-tck/src/main/java/net/java/html/js/tests/Global2String.java
----------------------------------------------------------------------
diff --git a/json-tck/src/main/java/net/java/html/js/tests/Global2String.java b/json-tck/src/main/java/net/java/html/js/tests/Global2String.java
new file mode 100644
index 0000000..afd847c
--- /dev/null
+++ b/json-tck/src/main/java/net/java/html/js/tests/Global2String.java
@@ -0,0 +1,52 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.js.tests;
+
+import net.java.html.js.JavaScriptBody;
+import net.java.html.js.JavaScriptResource;
+
+@JavaScriptResource("global2.js")
+class Global2String {
+    @JavaScriptBody(args = {}, body = "return global2String;")
+    public static native String init();
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json-tck/src/main/java/net/java/html/js/tests/GlobalString.java
----------------------------------------------------------------------
diff --git a/json-tck/src/main/java/net/java/html/js/tests/GlobalString.java b/json-tck/src/main/java/net/java/html/js/tests/GlobalString.java
new file mode 100644
index 0000000..fe46bff
--- /dev/null
+++ b/json-tck/src/main/java/net/java/html/js/tests/GlobalString.java
@@ -0,0 +1,52 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.js.tests;
+
+import net.java.html.js.JavaScriptBody;
+import net.java.html.js.JavaScriptResource;
+
+@JavaScriptResource("global.js")
+class GlobalString {
+    @JavaScriptBody(args = {}, body = "return globalString;")
+    public static native String init();
+}


[10/24] incubator-netbeans-html4j git commit: [INFRA-15006] Initial donation of HTML/Java NetBeans API. Equivalent to the content of html4j-donation-review.zip donated as part of ApacheNetBeansDonation1.zip with SHA256 being 7f2ca0f61953a190613c9a0fbcc1b

Posted by jt...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java
----------------------------------------------------------------------
diff --git a/json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java b/json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java
new file mode 100644
index 0000000..b719a20
--- /dev/null
+++ b/json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java
@@ -0,0 +1,2286 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.json.impl;
+
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.lang.annotation.AnnotationTypeMismatchException;
+import java.lang.annotation.IncompleteAnnotationException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.Completion;
+import javax.annotation.processing.Completions;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.Processor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.annotation.processing.SupportedSourceVersion;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.MirroredTypeException;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+import javax.tools.Diagnostic;
+import javax.tools.FileObject;
+import net.java.html.json.ComputedProperty;
+import net.java.html.json.Function;
+import net.java.html.json.Model;
+import net.java.html.json.ModelOperation;
+import net.java.html.json.OnPropertyChange;
+import net.java.html.json.OnReceive;
+import net.java.html.json.Property;
+import org.openide.util.lookup.ServiceProvider;
+
+/** Annotation processor to process {@link Model @Model} annotations and
+ * generate appropriate model classes.
+ *
+ * @author Jaroslav Tulach
+ */
+@ServiceProvider(service=Processor.class)
+@SupportedSourceVersion(SourceVersion.RELEASE_6)
+@SupportedAnnotationTypes({
+    "net.java.html.json.Model",
+    "net.java.html.json.ModelOperation",
+    "net.java.html.json.Function",
+    "net.java.html.json.OnReceive",
+    "net.java.html.json.OnPropertyChange",
+    "net.java.html.json.ComputedProperty",
+    "net.java.html.json.Property"
+})
+public final class ModelProcessor extends AbstractProcessor {
+    private static final Logger LOG = Logger.getLogger(ModelProcessor.class.getName());
+    private final Map<Element,String> models = new WeakHashMap<Element,String>();
+    private final Map<Element,Prprt[]> verify = new WeakHashMap<Element,Prprt[]>();
+    @Override
+    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+        boolean ok = true;
+        for (Element e : roundEnv.getElementsAnnotatedWith(Model.class)) {
+            if (!processModel(e)) {
+                ok = false;
+            }
+        }
+        if (roundEnv.processingOver()) {
+            models.clear();
+            for (Map.Entry<Element, Prprt[]> entry : verify.entrySet()) {
+                TypeElement te = (TypeElement)entry.getKey();
+                String fqn = te.getQualifiedName().toString();
+                Element finalElem = processingEnv.getElementUtils().getTypeElement(fqn);
+                if (finalElem == null) {
+                    continue;
+                }
+                Prprt[] props;
+                Model m = finalElem.getAnnotation(Model.class);
+                if (m == null) {
+                    continue;
+                }
+                props = Prprt.wrap(processingEnv, finalElem, m.properties());
+                for (Prprt p : props) {
+                    boolean[] isModel = { false };
+                    boolean[] isEnum = { false };
+                    boolean[] isPrimitive = { false };
+                    String t = checkType(p, isModel, isEnum, isPrimitive);
+                    if (isEnum[0]) {
+                        continue;
+                    }
+                    if (isPrimitive[0]) {
+                        continue;
+                    }
+                    if (isModel[0]) {
+                        continue;
+                    }
+                    if ("java.lang.String".equals(t)) {
+                        continue;
+                    }
+                    error("The type " + t + " should be defined by @Model annotation", entry.getKey());
+                }
+            }
+            verify.clear();
+        }
+        return ok;
+    }
+
+    private void error(String msg, Element e) {
+        processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, msg, e);
+    }
+
+    private boolean processModel(Element e) {
+        boolean ok = true;
+        Model m = e.getAnnotation(Model.class);
+        if (m == null) {
+            return true;
+        }
+        String pkg = findPkgName(e);
+        Writer w;
+        String className = m.className();
+        models.put(e, className);
+        try {
+            StringWriter body = new StringWriter();
+            StringBuilder onReceiveType = new StringBuilder();
+            List<GetSet> propsGetSet = new ArrayList<GetSet>();
+            List<Object> functions = new ArrayList<Object>();
+            Map<String, Collection<String[]>> propsDeps = new HashMap<String, Collection<String[]>>();
+            Map<String, Collection<String>> functionDeps = new HashMap<String, Collection<String>>();
+            Prprt[] props = createProps(e, m.properties());
+            final String builderPrefix = findBuilderPrefix(e, m);
+
+            if (!generateComputedProperties(className, body, props, e.getEnclosedElements(), propsGetSet, propsDeps)) {
+                ok = false;
+            }
+            if (!generateOnChange(e, propsDeps, props, className, functionDeps)) {
+                ok = false;
+            }
+            if (!generateProperties(e, builderPrefix, body, className, props, propsGetSet, propsDeps, functionDeps)) {
+                ok = false;
+            }
+            if (!generateFunctions(e, body, className, e.getEnclosedElements(), functions)) {
+                ok = false;
+            }
+            int functionsCount = functions.size() / 2;
+            for (int i = 0; i < functions.size(); i += 2) {
+                for (Prprt p : props) {
+                    if (p.name().equals(functions.get(i))) {
+                        error("Function cannot have the name of an existing property", e);
+                        ok = false;
+                    }
+                }
+            }
+            if (!generateReceive(e, body, className, e.getEnclosedElements(), onReceiveType)) {
+                ok = false;
+            }
+            if (!generateOperation(e, body, className, e.getEnclosedElements(), functions)) {
+                ok = false;
+            }
+            FileObject java = processingEnv.getFiler().createSourceFile(pkg + '.' + className, e);
+            w = new OutputStreamWriter(java.openOutputStream());
+            try {
+                w.append("package " + pkg + ";\n");
+                w.append("import net.java.html.json.*;\n");
+                final String inPckName = inPckName(e, false);
+                w.append("/** Generated for {@link ").append(inPckName).append("}*/\n");
+                w.append("public final class ").append(className).append(" implements Cloneable {\n");
+                w.append("  private static Class<").append(inPckName).append("> modelFor() { return ").append(inPckName).append(".class; }\n");
+                w.append("  private static final Html4JavaType TYPE = new Html4JavaType();\n");
+                if (m.instance()) {
+                    int cCnt = 0;
+                    for (Element c : e.getEnclosedElements()) {
+                        if (c.getKind() != ElementKind.CONSTRUCTOR) {
+                            continue;
+                        }
+                        cCnt++;
+                        ExecutableElement ec = (ExecutableElement) c;
+                        if (ec.getParameters().size() > 0) {
+                            continue;
+                        }
+                        if (ec.getModifiers().contains(Modifier.PRIVATE)) {
+                            continue;
+                        }
+                        cCnt = 0;
+                        break;
+                    }
+                    if (cCnt > 0) {
+                        ok = false;
+                        error("Needs non-private default constructor when instance=true", e);
+                        w.append("  private final ").append(inPckName).append(" instance = null;\n");
+                    } else {
+                        w.append("  private final ").append(inPckName).append(" instance = new ").append(inPckName).append("();\n");
+                    }
+                }
+                w.append("  private final org.netbeans.html.json.spi.Proto proto;\n");
+                w.append(body.toString());
+                w.append("  private ").append(className).append("(net.java.html.BrwsrCtx context) {\n");
+                w.append("    this.proto = TYPE.createProto(this, context);\n");
+                for (Prprt p : props) {
+                    if (p.array()) {
+                        final String tn = typeName(p);
+                        String[] gs = toGetSet(p.name(), tn, p.array());
+                        w.write("    this.prop_" + p.name() + " = proto.createList(\""
+                            + p.name() + "\"");
+                        if (p.mutable()) {
+                            if (functionDeps.containsKey(p.name())) {
+                                int index = Arrays.asList(functionDeps.keySet().toArray()).indexOf(p.name());
+                                w.write(", " + index);
+                            } else {
+                                w.write(", -1");
+                            }
+                        } else {
+                            w.write(", java.lang.Integer.MIN_VALUE");
+                        }
+                        Collection<String[]> dependants = propsDeps.get(p.name());
+                        if (dependants != null) {
+                            for (String[] depProp : dependants) {
+                                w.write(", ");
+                                w.write('\"');
+                                w.write(depProp[0]);
+                                w.write('\"');
+                            }
+                        }
+                        w.write(")");
+                        w.write(";\n");
+                    }
+                }
+                w.append("  };\n");
+                w.append("  public ").append(className).append("() {\n");
+                w.append("    this(net.java.html.BrwsrCtx.findDefault(").append(className).append(".class));\n");
+                for (Prprt p : props) {
+                    if (!p.array()) {
+                        boolean[] isModel = {false};
+                        boolean[] isEnum = {false};
+                        boolean isPrimitive[] = {false};
+                        String tn = checkType(p, isModel, isEnum, isPrimitive);
+                        if (isModel[0]) {
+                            w.write("    prop_" + p.name() + " = this;\n");
+                        }
+                    }
+                }
+                w.append("  };\n");
+                if (props.length > 0 && builderPrefix == null) {
+                    StringBuilder constructorWithArguments = new StringBuilder();
+                    constructorWithArguments.append("  public ").append(className).append("(");
+                    Prprt firstArray = null;
+                    String sep = "";
+                    int parameterCount = 0;
+                    for (Prprt p : props) {
+                        if (p.array()) {
+                            if (firstArray == null) {
+                                firstArray = p;
+                            }
+                            continue;
+                        }
+                        String tn = typeName(p);
+                        constructorWithArguments.append(sep);
+                        constructorWithArguments.append(tn);
+                        String[] third = toGetSet(p.name(), tn, false);
+                        constructorWithArguments.append(" ").append(third[2]);
+                        sep = ", ";
+                        parameterCount++;
+                    }
+                    if (firstArray != null) {
+                        String tn;
+                        boolean[] isModel = {false};
+                        boolean[] isEnum = {false};
+                        boolean isPrimitive[] = {false};
+                        tn = checkType(firstArray, isModel, isEnum, isPrimitive);
+                        constructorWithArguments.append(sep);
+                        constructorWithArguments.append(tn);
+                        String[] third = toGetSet(firstArray.name(), tn, true);
+                        constructorWithArguments.append("... ").append(third[2]);
+                        parameterCount++;
+                    }
+                    constructorWithArguments.append(") {\n");
+                    constructorWithArguments.append("    this(net.java.html.BrwsrCtx.findDefault(").append(className).append(".class));\n");
+                    for (Prprt p : props) {
+                        if (p.array()) {
+                            continue;
+                        }
+                        String[] third = toGetSet(p.name(), null, false);
+                        constructorWithArguments.append("    this.prop_" + p.name() + " = " + third[2] + ";\n");
+                    }
+                    if (firstArray != null) {
+                        String[] third = toGetSet(firstArray.name(), null, true);
+                        constructorWithArguments.append("    proto.initTo(this.prop_" + firstArray.name() + ", " + third[2] + ");\n");
+                    }
+                    constructorWithArguments.append("  };\n");
+                    if (parameterCount < 255) {
+                        w.write(constructorWithArguments.toString());
+                    }
+                }
+                w.append("  private static class Html4JavaType extends org.netbeans.html.json.spi.Proto.Type<").append(className).append("> {\n");
+                w.append("    private Html4JavaType() {\n      super(").append(className).append(".class, ").
+                    append(inPckName).append(".class, " + propsGetSet.size() + ", "
+                    + functionsCount + ");\n");
+                {
+                    for (int i = 0; i < propsGetSet.size(); i++) {
+                        w.append("      registerProperty(\"").append(propsGetSet.get(i).name).append("\", ");
+                        w.append((i) + ", " + propsGetSet.get(i).readOnly + ", " + propsGetSet.get(i).constant + ");\n");
+                    }
+                }
+                {
+                    for (int i = 0; i < functionsCount; i++) {
+                        w.append("      registerFunction(\"").append((String)functions.get(i * 2)).append("\", ");
+                        w.append(i + ");\n");
+                    }
+                }
+                w.append("    }\n");
+                w.append("    @Override public void setValue(" + className + " data, int type, Object value) {\n");
+                w.append("      switch (type) {\n");
+                for (int i = 0; i < propsGetSet.size(); i++) {
+                    final GetSet pgs = propsGetSet.get(i);
+                    if (pgs.readOnly) {
+                        continue;
+                    }
+                    final String set = pgs.setter;
+                    String tn = pgs.type;
+                    String btn = findBoxedType(tn);
+                    if (btn != null) {
+                        tn = btn;
+                    }
+                    w.append("        case " + i + ": ");
+                    if (pgs.setter != null) {
+                        w.append("data.").append(pgs.setter).append("(TYPE.extractValue(" + tn + ".class, value)); return;\n");
+                    } else {
+                        w.append("TYPE.replaceValue(data.").append(pgs.getter).append("(), " + tn + ".class, value); return;\n");
+                    }
+                }
+                w.append("      }\n");
+                w.append("      throw new UnsupportedOperationException();\n");
+                w.append("    }\n");
+                w.append("    @Override public Object getValue(" + className + " data, int type) {\n");
+                w.append("      switch (type) {\n");
+                for (int i = 0; i < propsGetSet.size(); i++) {
+                    final String get = propsGetSet.get(i).getter;
+                    if (get != null) {
+                        w.append("        case " + i + ": return data." + get + "();\n");
+                    }
+                }
+                w.append("      }\n");
+                w.append("      throw new UnsupportedOperationException();\n");
+                w.append("    }\n");
+                w.append("    @Override public void call(" + className + " model, int type, Object data, Object ev) throws Exception {\n");
+                w.append("      switch (type) {\n");
+                for (int i = 0; i < functions.size(); i += 2) {
+                    final String name = (String)functions.get(i);
+                    final Object param = functions.get(i + 1);
+                    if (param instanceof ExecutableElement) {
+                        ExecutableElement ee = (ExecutableElement)param;
+                        w.append("        case " + (i / 2) + ":\n");
+                        w.append("          ");
+                        if (m.instance()) {
+                            w.append("model.instance");
+                        } else {
+                            w.append(((TypeElement)e).getQualifiedName());
+                        }
+                        w.append(".").append(name).append("(");
+                        w.append(wrapParams(ee, null, className, "model", "ev", "data"));
+                        w.append(");\n");
+                        w.append("          return;\n");
+                    } else {
+                        String call = (String)param;
+                        w.append("        case " + (i / 2) + ":\n"); // model." + name + "(data, ev); return;\n");
+                        w.append("          ").append(call).append("\n");
+                        w.append("          return;\n");
+
+                    }
+                }
+                w.append("      }\n");
+                w.append("      throw new UnsupportedOperationException();\n");
+                w.append("    }\n");
+                w.append("    @Override public org.netbeans.html.json.spi.Proto protoFor(Object obj) {\n");
+                w.append("      return ((" + className + ")obj).proto;");
+                w.append("    }\n");
+                w.append("    @Override public void onChange(" + className + " model, int type) {\n");
+                w.append("      switch (type) {\n");
+                {
+                    String[] arr = functionDeps.keySet().toArray(new String[0]);
+                    for (int i = 0; i < arr.length; i++) {
+                        Collection<String> onChange = functionDeps.get(arr[i]);
+                        if (onChange != null) {
+                            w.append("      case " + i + ":\n");
+                            for (String call : onChange) {
+                                w.append("      ").append(call).append("\n");
+                            }
+                            w.write("      return;\n");
+                        }
+                    }
+                }
+                w.append("    }\n");
+                w.append("      throw new UnsupportedOperationException();\n");
+                w.append("    }\n");
+                w.append(onReceiveType);
+                w.append("    @Override public " + className + " read(net.java.html.BrwsrCtx c, Object json) { return new " + className + "(c, json); }\n");
+                w.append("    @Override public " + className + " cloneTo(" + className + " o, net.java.html.BrwsrCtx c) { return o.clone(c); }\n");
+                w.append("  }\n");
+                w.append("  private ").append(className).append("(net.java.html.BrwsrCtx c, Object json) {\n");
+                w.append("    this(c);\n");
+                int values = 0;
+                for (int i = 0; i < propsGetSet.size(); i++) {
+                    Prprt p = findPrprt(props, propsGetSet.get(i).name);
+                    if (p == null) {
+                        continue;
+                    }
+                    values++;
+                }
+                w.append("    Object[] ret = new Object[" + values + "];\n");
+                w.append("    proto.extract(json, new String[] {\n");
+                for (int i = 0; i < propsGetSet.size(); i++) {
+                    Prprt p = findPrprt(props, propsGetSet.get(i).name);
+                    if (p == null) {
+                        continue;
+                    }
+                    w.append("      \"").append(propsGetSet.get(i).name).append("\",\n");
+                }
+                w.append("    }, ret);\n");
+                for (int i = 0, cnt = 0, prop = 0; i < propsGetSet.size(); i++) {
+                    final String pn = propsGetSet.get(i).name;
+                    Prprt p = findPrprt(props, pn);
+                    if (p == null || prop >= props.length) {
+                        continue;
+                    }
+                    boolean[] isModel = { false };
+                    boolean[] isEnum = { false };
+                    boolean isPrimitive[] = { false };
+                    String type = checkType(props[prop++], isModel, isEnum, isPrimitive);
+                    if (p.array()) {
+                        w.append("    for (Object e : useAsArray(ret[" + cnt + "])) {\n");
+                        if (isModel[0]) {
+                            w.append("      this.prop_").append(pn).append(".add(proto.read");
+                            w.append("(" + type + ".class, e));\n");
+                        } else if (isEnum[0]) {
+                            w.append("        this.prop_").append(pn);
+                            w.append(".add(e == null ? null : ");
+                            w.append(type).append(".valueOf(TYPE.stringValue(e)));\n");
+                        } else {
+                            if (isPrimitive(type)) {
+                                if (type.equals("char")) {
+                                    w.append("        this.prop_").append(pn).append(".add(TYPE.charValue(e));\n");
+                                } else if (type.equals("boolean")) {
+                                    w.append("        this.prop_").append(pn).append(".add(TYPE.boolValue(e));\n");
+                                } else {
+                                    w.append("        this.prop_").append(pn).append(".add(TYPE.numberValue(e).");
+                                    w.append(type).append("Value());\n");
+                                }
+                            } else {
+                                w.append("        this.prop_").append(pn).append(".add((");
+                                w.append(type).append(")e);\n");
+                            }
+                        }
+                        w.append("    }\n");
+                    } else {
+                        if (isEnum[0]) {
+                            w.append("    try {\n");
+                            w.append("    this.prop_").append(pn);
+                            w.append(" = ret[" + cnt + "] == null ? null : ");
+                            w.append(type).append(".valueOf(TYPE.stringValue(ret[" + cnt + "]));\n");
+                            w.append("    } catch (IllegalArgumentException ex) {\n");
+                            w.append("      ex.printStackTrace();\n");
+                            w.append("    }\n");
+                        } else if (isPrimitive(type)) {
+                            w.append("    this.prop_").append(pn);
+                            w.append(" = ret[" + cnt + "] == null ? ");
+                            if ("char".equals(type)) {
+                                w.append("0 : (TYPE.charValue(");
+                            } else if ("boolean".equals(type)) {
+                                w.append("false : (TYPE.boolValue(");
+                            } else {
+                                w.append("0 : (TYPE.numberValue(");
+                            }
+                            w.append("ret[" + cnt + "])).");
+                            w.append(type).append("Value();\n");
+                        } else if (isModel[0]) {
+                            w.append("    this.prop_").append(pn).append(" = proto.read");
+                            w.append("(" + type + ".class, ");
+                            w.append("ret[" + cnt + "]);\n");
+                        }else {
+                            w.append("    this.prop_").append(pn);
+                            w.append(" = (").append(type).append(')');
+                            w.append("ret[" + cnt + "];\n");
+                        }
+                    }
+                    cnt++;
+                }
+                w.append("  }\n");
+                w.append("  private static Object[] useAsArray(Object o) {\n");
+                w.append("    return o instanceof Object[] ? ((Object[])o) : o == null ? new Object[0] : new Object[] { o };\n");
+                w.append("  }\n");
+                writeToString(props, w);
+                writeClone(className, props, w);
+                String targetId = findTargetId(e);
+                if (targetId != null) {
+                    w.write("  /** Activates this model instance in the current {@link \n"
+                        + "net.java.html.json.Models#bind(java.lang.Object, net.java.html.BrwsrCtx) browser context}. \n"
+                        + "In case of using Knockout technology, this means to \n"
+                        + "bind JSON like data in this model instance with Knockout tags in \n"
+                        + "the surrounding HTML page.\n"
+                    );
+                    if (targetId != null) {
+                        w.write("This method binds to element '" + targetId + "' on the page\n");
+                    }
+                    w.write(""
+                        + "@return <code>this</code> object\n"
+                        + "*/\n"
+                    );
+                    w.write("  public " + className + " applyBindings() {\n");
+                    w.write("    proto.applyBindings();\n");
+    //                w.write("    proto.applyBindings(id);\n");
+                    w.write("    return this;\n");
+                    w.write("  }\n");
+                } else {
+                    w.write("  private " + className + " applyBindings() {\n");
+                    w.write("    throw new IllegalStateException(\"Please specify targetId=\\\"\\\" in your @Model annotation\");\n");
+                    w.write("  }\n");
+                }
+                w.write("  public boolean equals(Object o) {\n");
+                w.write("    if (o == this) return true;\n");
+                w.write("    if (!(o instanceof " + className + ")) return false;\n");
+                w.write("    " + className + " p = (" + className + ")o;\n");
+                boolean thisToNull = false;
+                for (Prprt p : props) {
+                    boolean[] isModel = {false};
+                    boolean[] isEnum = {false};
+                    boolean isPrimitive[] = {false};
+                    checkType(p, isModel, isEnum, isPrimitive);
+                    if (isModel[0]) {
+                        w.write("    if (!TYPE.isSame(thisToNull(prop_" + p.name() + "), p.thisToNull(p.prop_" + p.name() + "))) return false;\n");
+                        thisToNull = true;
+                    } else {
+                        w.write("    if (!TYPE.isSame(prop_" + p.name() + ", p.prop_" + p.name() + ")) return false;\n");
+                    }
+                }
+                w.write("    return true;\n");
+                w.write("  }\n");
+                w.write("  public int hashCode() {\n");
+                w.write("    int h = " + className + ".class.getName().hashCode();\n");
+                for (Prprt p : props) {
+                    boolean[] isModel = {false};
+                    boolean[] isEnum = {false};
+                    boolean isPrimitive[] = {false};
+                    checkType(p, isModel, isEnum, isPrimitive);
+                    if (isModel[0]) {
+                        w.write("    h = TYPE.hashPlus(thisToNull(prop_" + p.name() + "), h);\n");
+                    } else {
+                        w.write("    h = TYPE.hashPlus(prop_" + p.name() + ", h);\n");
+                    }
+                }
+                w.write("    return h;\n");
+                w.write("  }\n");
+                if (thisToNull) {
+                    w.write("  private Object thisToNull(Object value) {\n");
+                    w.write("    return value == this ? null : value;\n");
+                    w.write("  }\n");
+                }
+                w.write("}\n");
+            } finally {
+                w.close();
+            }
+        } catch (IOException ex) {
+            error("Can't create " + className + ".java", e);
+            return false;
+        }
+        return ok;
+    }
+
+    private static String findBuilderPrefix(Element e, Model m) {
+        if (!m.builder().isEmpty()) {
+            return m.builder();
+        }
+        for (AnnotationMirror am : e.getAnnotationMirrors()) {
+            for (Map.Entry<? extends Object, ? extends Object> entry : am.getElementValues().entrySet()) {
+                if ("builder()".equals(entry.getKey().toString())) {
+                    return "";
+                }
+            }
+        }
+        return null;
+    }
+
+    private static String builderMethod(String builderPrefix, Prprt p) {
+        if (builderPrefix.isEmpty()) {
+            return p.name();
+        }
+        return builderPrefix + Character.toUpperCase(p.name().charAt(0)) + p.name().substring(1);
+    }
+
+    private boolean generateProperties(
+        Element where, String builderPrefix,
+        Writer w, String className, Prprt[] properties,
+        List<GetSet> props,
+        Map<String,Collection<String[]>> deps,
+        Map<String,Collection<String>> functionDeps
+    ) throws IOException {
+        boolean ok = true;
+        for (Prprt p : properties) {
+            final String tn;
+            tn = typeName(p);
+            String[] gs = toGetSet(p.name(), tn, p.array());
+            String castTo;
+
+            if (p.array()) {
+                w.write("  private final java.util.List<" + tn + "> prop_" + p.name() + ";\n");
+
+                castTo = "java.util.List";
+                w.write("  public java.util.List<" + tn + "> " + gs[0] + "() {\n");
+                w.write("    proto.accessProperty(\"" + p.name() + "\");\n");
+                w.write("    return prop_" + p.name() + ";\n");
+                w.write("  }\n");
+                if (builderPrefix != null) {
+                    boolean[] isModel = {false};
+                    boolean[] isEnum = {false};
+                    boolean isPrimitive[] = {false};
+                    String ret = checkType(p, isModel, isEnum, isPrimitive);
+                    w.write("  public " + className + " " + builderMethod(builderPrefix, p) + "(" + ret + "... v) {\n");
+                    w.write("    proto.accessProperty(\"" + p.name() + "\");\n");
+                    w.append("   TYPE.replaceValue(prop_").append(p.name()).append(", " + tn + ".class, v);\n");
+                    w.write("    return this;\n");
+                    w.write("  }\n");
+                }
+            } else {
+                castTo = tn;
+                boolean isModel[] = { false };
+                boolean isEnum[] = { false };
+                boolean isPrimitive[] = { false };
+                checkType(p, isModel, isEnum, isPrimitive);
+                if (isModel[0]) {
+                    w.write("  private /*" + tn + "*/Object prop_" + p.name() + ";\n");
+
+                } else {
+                    w.write("  private " + tn + " prop_" + p.name() + ";\n");
+                }
+                w.write("  public " + tn + " " + gs[0] + "() {\n");
+                w.write("    proto.accessProperty(\"" + p.name() + "\");\n");
+                if (isModel[0]) {
+                    w.write("    if (prop_" + p.name() + " == this) prop_" + p.name() + " = new " + tn +"();\n");
+                }
+                w.write("    return (" + tn + ")prop_" + p.name() + ";\n");
+                w.write("  }\n");
+                w.write("  public void " + gs[1] + "(" + tn + " v) {\n");
+                if (!p.mutable()) {
+                    w.write("    proto.initTo(null, null);\n");
+                }
+                w.write("    proto.verifyUnlocked();\n");
+                w.write("    Object o = prop_" + p.name() + ";\n");
+                if (isModel[0]) {
+                    w.write("    if (o == v) return;\n");
+                    w.write("    prop_" + p.name() + " = v;\n");
+                } else {
+                    w.write("    if (TYPE.isSame(o , v)) return;\n");
+                    w.write("    prop_" + p.name() + " = v;\n");
+                }
+                w.write("    proto.valueHasMutated(\"" + p.name() + "\", o, v);\n");
+                {
+                    Collection<String[]> dependants = deps.get(p.name());
+                    if (dependants != null) {
+                        for (String[] pair : dependants) {
+                            w.write("    proto.valueHasMutated(\"" + pair[0] + "\", null, " + pair[1] + "());\n");
+                        }
+                    }
+                }
+                {
+                    Collection<String> dependants = functionDeps.get(p.name());
+                    if (dependants != null) {
+                        w.append("    ");
+                        w.append(className).append(" model = ").append(className).append(".this;\n");
+                        for (String call : dependants) {
+                            w.append("  ").append(call);
+                        }
+                    }
+                }
+                w.write("  }\n");
+                if (builderPrefix != null) {
+                    w.write("  public " + className + " " + builderMethod(builderPrefix, p) + "(" + tn + " v) {\n");
+                    w.write("    " + gs[1] + "(v);\n");
+                    w.write("    return this;\n");
+                    w.write("  }\n");
+                }
+            }
+
+            for (int i = 0; i < props.size(); i++) {
+                if (props.get(i).name.equals(p.name())) {
+                    error("Cannot have the property " + p.name() + " defined twice", where);
+                    ok = false;
+                }
+            }
+
+            props.add(new GetSet(
+                p.name(),
+                gs[0],
+                gs[1],
+                tn,
+                gs[3] == null && !p.array(),
+                !p.mutable()
+            ));
+        }
+        return ok;
+    }
+
+    private boolean generateComputedProperties(
+        String className,
+        Writer w, Prprt[] fixedProps,
+        Collection<? extends Element> arr, Collection<GetSet> props,
+        Map<String,Collection<String[]>> deps
+    ) throws IOException {
+        boolean ok = true;
+        NEXT_ANNOTATION: for (Element e : arr) {
+            if (e.getKind() != ElementKind.METHOD) {
+                continue;
+            }
+            final ComputedProperty cp = e.getAnnotation(ComputedProperty.class);
+            final Transitive tp = e.getAnnotation(Transitive.class);
+            if (cp == null) {
+                continue;
+            }
+            if (!e.getModifiers().contains(Modifier.STATIC)) {
+                error("Method " + e.getSimpleName() + " has to be static when annotated by @ComputedProperty", e);
+                ok = false;
+                continue;
+            }
+            ExecutableElement ee = (ExecutableElement)e;
+            ExecutableElement write = null;
+            if (!cp.write().isEmpty()) {
+                write = findWrite(ee, (TypeElement)e.getEnclosingElement(), cp.write(), className);
+                ok = write != null;
+            }
+            final TypeMirror rt = ee.getReturnType();
+            final Types tu = processingEnv.getTypeUtils();
+            TypeMirror ert = tu.erasure(rt);
+            String tn = fqn(ert, ee);
+            boolean array = false;
+            final TypeMirror toCheck;
+            if (tn.equals("java.util.List")) {
+                array = true;
+                toCheck = ((DeclaredType)rt).getTypeArguments().get(0);
+            } else {
+                toCheck = rt;
+            }
+
+            final String sn = ee.getSimpleName().toString();
+
+            if (toCheck.getKind().isPrimitive()) {
+                // OK
+            } else {
+                TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType();
+                TypeMirror enumType = processingEnv.getElementUtils().getTypeElement("java.lang.Enum").asType();
+
+                if (tu.isSubtype(toCheck, stringType)) {
+                    // OK
+                } else if (tu.isSubtype(tu.erasure(toCheck), tu.erasure(enumType))) {
+                    // OK
+                } else if (isModel(toCheck)) {
+                    // OK
+                } else {
+                    try {
+                        tu.unboxedType(toCheck);
+                        // boxed types are OK
+                    } catch (IllegalArgumentException ex) {
+                        ok = false;
+                        error(sn + " cannot return " + toCheck, e);
+                    }
+                }
+            }
+
+            final String propertyName = e.getSimpleName().toString();
+            for (GetSet prop : props) {
+                if (propertyName.equals(prop.name)) {
+                    error("Cannot have the property " + propertyName + " defined twice", e);
+                    ok = false;
+                    continue NEXT_ANNOTATION;
+                }
+            }
+
+            String[] gs = toGetSet(sn, tn, array);
+
+            w.write("  public " + tn);
+            if (array) {
+                w.write("<" + toCheck + ">");
+            }
+            w.write(" " + gs[0] + "() {\n");
+            int arg = 0;
+            boolean deep = false;
+            for (VariableElement pe : ee.getParameters()) {
+                final String dn = pe.getSimpleName().toString();
+
+                if (!verifyPropName(pe, dn, fixedProps)) {
+                    ok = false;
+                }
+                final TypeMirror pt = pe.asType();
+                if (isModel(pt)) {
+                    deep = true;
+                }
+                final String dt = fqn(pt, ee);
+                if (dt.startsWith("java.util.List") && pt instanceof DeclaredType) {
+                    final List<? extends TypeMirror> ptArgs = ((DeclaredType)pt).getTypeArguments();
+                    if (ptArgs.size() == 1 && isModel(ptArgs.get(0))) {
+                        deep = true;
+                    }
+                }
+                String[] call = toGetSet(dn, dt, false);
+                w.write("    " + dt + " arg" + (++arg) + " = ");
+                w.write(call[0] + "();\n");
+
+                Collection<String[]> depends = deps.get(dn);
+                if (depends == null) {
+                    depends = new LinkedHashSet<String[]>();
+                    deps.put(dn, depends);
+                }
+                depends.add(new String[] { sn, gs[0] });
+            }
+            w.write("    try {\n");
+            if (tp != null) {
+                deep = tp.deep();
+            }
+            if (deep) {
+                w.write("      proto.acquireLock(\"" + sn + "\");\n");
+            } else {
+                w.write("      proto.acquireLock();\n");
+            }
+            w.write("      return " + fqn(ee.getEnclosingElement().asType(), ee) + '.' + e.getSimpleName() + "(");
+            String sep = "";
+            for (int i = 1; i <= arg; i++) {
+                w.write(sep);
+                w.write("arg" + i);
+                sep = ", ";
+            }
+            w.write(");\n");
+            w.write("    } finally {\n");
+            w.write("      proto.releaseLock();\n");
+            w.write("    }\n");
+            w.write("  }\n");
+
+            if (write == null) {
+                props.add(new GetSet(
+                    propertyName,
+                    gs[0],
+                    null,
+                    tn,
+                    true,
+                    false
+                ));
+            } else {
+                w.write("  public void " + gs[4] + "(" + write.getParameters().get(1).asType());
+                w.write(" value) {\n");
+                w.write("    " + fqn(ee.getEnclosingElement().asType(), ee) + '.' + write.getSimpleName() + "(this, value);\n");
+                w.write("  }\n");
+
+                props.add(new GetSet(
+                    propertyName,
+                    gs[0],
+                    gs[4],
+                    tn,
+                    false,
+                    false
+                ));
+            }
+        }
+
+        return ok;
+    }
+
+    private static String[] toGetSet(String name, String type, boolean array) {
+        String n = Character.toUpperCase(name.charAt(0)) + name.substring(1);
+        boolean clazz = "class".equals(name);
+        String pref = clazz ? "access" : "get";
+        if ("boolean".equals(type) && !array) {
+            pref = "is";
+        }
+        if (array) {
+            return new String[] {
+                pref + n,
+                null,
+                "a" + n,
+                null,
+                "set" + n
+            };
+        }
+        return new String[]{
+            pref + n,
+            "set" + n,
+            "a" + n,
+            "",
+            "set" + n
+        };
+    }
+
+    private String typeName(Prprt p) {
+        String ret;
+        boolean[] isModel = { false };
+        boolean[] isEnum = { false };
+        boolean isPrimitive[] = { false };
+        ret = checkType(p, isModel, isEnum, isPrimitive);
+        if (p.array()) {
+            String bt = findBoxedType(ret);
+            if (bt != null) {
+                return bt;
+            }
+        }
+        return ret;
+    }
+
+    private static String findBoxedType(String ret) {
+        if (ret.equals("boolean")) {
+            return Boolean.class.getName();
+        }
+        if (ret.equals("byte")) {
+            return Byte.class.getName();
+        }
+        if (ret.equals("short")) {
+            return Short.class.getName();
+        }
+        if (ret.equals("char")) {
+            return Character.class.getName();
+        }
+        if (ret.equals("int")) {
+            return Integer.class.getName();
+        }
+        if (ret.equals("long")) {
+            return Long.class.getName();
+        }
+        if (ret.equals("float")) {
+            return Float.class.getName();
+        }
+        if (ret.equals("double")) {
+            return Double.class.getName();
+        }
+        return null;
+    }
+
+    private boolean verifyPropName(Element e, String propName, Prprt[] existingProps) {
+        StringBuilder sb = new StringBuilder();
+        String sep = "";
+        for (Prprt Prprt : existingProps) {
+            if (Prprt.name().equals(propName)) {
+                return true;
+            }
+            sb.append(sep);
+            sb.append('"');
+            sb.append(Prprt.name());
+            sb.append('"');
+            sep = ", ";
+        }
+        error(
+            propName + " is not one of known properties: " + sb
+            , e
+        );
+        return false;
+    }
+
+    private static String findPkgName(Element e) {
+        for (;;) {
+            if (e.getKind() == ElementKind.PACKAGE) {
+                return ((PackageElement)e).getQualifiedName().toString();
+            }
+            e = e.getEnclosingElement();
+        }
+    }
+
+    private boolean generateFunctions(
+        Element clazz, StringWriter body, String className,
+        List<? extends Element> enclosedElements, List<Object> functions
+    ) {
+        boolean instance = clazz.getAnnotation(Model.class).instance();
+        for (Element m : enclosedElements) {
+            if (m.getKind() != ElementKind.METHOD) {
+                continue;
+            }
+            ExecutableElement e = (ExecutableElement)m;
+            Function onF = e.getAnnotation(Function.class);
+            if (onF == null) {
+                continue;
+            }
+            if (!instance && !e.getModifiers().contains(Modifier.STATIC)) {
+                error("@OnFunction method needs to be static", e);
+                return false;
+            }
+            if (e.getModifiers().contains(Modifier.PRIVATE)) {
+                error("@OnFunction method cannot be private", e);
+                return false;
+            }
+            if (e.getReturnType().getKind() != TypeKind.VOID) {
+                error("@OnFunction method should return void", e);
+                return false;
+            }
+            String n = e.getSimpleName().toString();
+            functions.add(n);
+            functions.add(e);
+        }
+        return true;
+    }
+
+    private boolean generateOnChange(Element clazz, Map<String,Collection<String[]>> propDeps,
+        Prprt[] properties, String className,
+        Map<String, Collection<String>> functionDeps
+    ) {
+        boolean instance = clazz.getAnnotation(Model.class).instance();
+        for (Element m : clazz.getEnclosedElements()) {
+            if (m.getKind() != ElementKind.METHOD) {
+                continue;
+            }
+            ExecutableElement e = (ExecutableElement) m;
+            OnPropertyChange onPC = e.getAnnotation(OnPropertyChange.class);
+            if (onPC == null) {
+                continue;
+            }
+            for (String pn : onPC.value()) {
+                if (findPrprt(properties, pn) == null && findDerivedFrom(propDeps, pn).isEmpty()) {
+                    error("No Prprt named '" + pn + "' in the model", clazz);
+                    return false;
+                }
+            }
+            if (!instance && !e.getModifiers().contains(Modifier.STATIC)) {
+                error("@OnPrprtChange method needs to be static", e);
+                return false;
+            }
+            if (e.getModifiers().contains(Modifier.PRIVATE)) {
+                error("@OnPrprtChange method cannot be private", e);
+                return false;
+            }
+            if (e.getReturnType().getKind() != TypeKind.VOID) {
+                error("@OnPrprtChange method should return void", e);
+                return false;
+            }
+            String n = e.getSimpleName().toString();
+
+
+            for (String pn : onPC.value()) {
+                StringBuilder call = new StringBuilder();
+                call.append("  ").append(inPckName(clazz, instance)).append(".").append(n).append("(");
+                call.append(wrapPropName(e, className, "name", pn));
+                call.append(");\n");
+
+                Collection<String> change = functionDeps.get(pn);
+                if (change == null) {
+                    change = new ArrayList<String>();
+                    functionDeps.put(pn, change);
+                }
+                change.add(call.toString());
+                for (String dpn : findDerivedFrom(propDeps, pn)) {
+                    change = functionDeps.get(dpn);
+                    if (change == null) {
+                        change = new ArrayList<String>();
+                        functionDeps.put(dpn, change);
+                    }
+                    change.add(call.toString());
+                }
+            }
+        }
+        return true;
+    }
+
+    private boolean generateOperation(Element clazz,
+        StringWriter body, String className,
+        List<? extends Element> enclosedElements,
+        List<Object> functions
+    ) {
+        boolean instance = clazz.getAnnotation(Model.class).instance();
+        for (Element m : enclosedElements) {
+            if (m.getKind() != ElementKind.METHOD) {
+                continue;
+            }
+            ExecutableElement e = (ExecutableElement)m;
+            ModelOperation mO = e.getAnnotation(ModelOperation.class);
+            if (mO == null) {
+                continue;
+            }
+            if (!instance && !e.getModifiers().contains(Modifier.STATIC)) {
+                error("@ModelOperation method needs to be static", e);
+                return false;
+            }
+            if (e.getModifiers().contains(Modifier.PRIVATE)) {
+                error("@ModelOperation method cannot be private", e);
+                return false;
+            }
+            if (e.getReturnType().getKind() != TypeKind.VOID) {
+                error("@ModelOperation method should return void", e);
+                return false;
+            }
+            List<String> args = new ArrayList<String>();
+            {
+                body.append("  /** @see " + clazz.getSimpleName() + "#" + m.getSimpleName() + " */\n");
+                body.append("  public void ").append(m.getSimpleName()).append("(");
+                String sep = "";
+                boolean checkFirst = true;
+                for (VariableElement ve : e.getParameters()) {
+                    final TypeMirror type = ve.asType();
+                    CharSequence simpleName;
+                    if (type.getKind() == TypeKind.DECLARED) {
+                        simpleName = ((DeclaredType)type).asElement().getSimpleName();
+                    } else {
+                        simpleName = type.toString();
+                    }
+                    if (checkFirst && simpleName.toString().equals(className)) {
+                        checkFirst = false;
+                    } else {
+                        if (checkFirst) {
+                            error("First parameter of @ModelOperation method must be " + className, m);
+                            return false;
+                        }
+                        args.add(ve.getSimpleName().toString());
+                        body.append(sep).append("final ");
+                        body.append(ve.asType().toString()).append(" ");
+                        body.append(ve.toString());
+                        sep = ", ";
+                    }
+                }
+                body.append(") {\n");
+                int idx = functions.size() / 2;
+                functions.add(m.getSimpleName().toString());
+                body.append("    proto.runInBrowser(" + idx);
+                for (String s : args) {
+                    body.append(", ").append(s);
+                }
+                body.append(");\n");
+                body.append("  }\n");
+
+                StringBuilder call = new StringBuilder();
+                call.append("{ Object[] arr = (Object[])data; ");
+                call.append(inPckName(clazz, true)).append(".").append(m.getSimpleName()).append("(");
+                int i = 0;
+                for (VariableElement ve : e.getParameters()) {
+                    if (i++ == 0) {
+                        call.append("model");
+                        continue;
+                    }
+                    String type = ve.asType().toString();
+                    String boxedType = findBoxedType(type);
+                    if (boxedType != null) {
+                        type = boxedType;
+                    }
+                    call.append(", ").append("(").append(type).append(")arr[").append(i - 2).append("]");
+                }
+                call.append("); }");
+                functions.add(call.toString());
+            }
+
+        }
+        return true;
+    }
+
+
+    private boolean generateReceive(
+        Element clazz, StringWriter body, String className,
+        List<? extends Element> enclosedElements, StringBuilder inType
+    ) {
+        boolean ret = generateReceiveImpl(clazz, body, className, enclosedElements, inType);
+        if (!ret) {
+            inType.setLength(0);
+        }
+        return ret;
+    }
+    private boolean generateReceiveImpl(
+        Element clazz, StringWriter body, String className,
+        List<? extends Element> enclosedElements, StringBuilder inType
+    ) {
+        inType.append("  @Override public void onMessage(").append(className).append(" model, int index, int type, Object data, Object[] params) {\n");
+        inType.append("    switch (index) {\n");
+        int index = 0;
+        boolean ok = true;
+        boolean instance = clazz.getAnnotation(Model.class).instance();
+        for (Element m : enclosedElements) {
+            if (m.getKind() != ElementKind.METHOD) {
+                continue;
+            }
+            ExecutableElement e = (ExecutableElement)m;
+            OnReceive onR = e.getAnnotation(OnReceive.class);
+            if (onR == null) {
+                continue;
+            }
+            if (!instance && !e.getModifiers().contains(Modifier.STATIC)) {
+                error("@OnReceive method needs to be static", e);
+                return false;
+            }
+            if (e.getModifiers().contains(Modifier.PRIVATE)) {
+                error("@OnReceive method cannot be private", e);
+                return false;
+            }
+            if (e.getReturnType().getKind() != TypeKind.VOID) {
+                error("@OnReceive method should return void", e);
+                return false;
+            }
+            if (!onR.jsonp().isEmpty() && !"GET".equals(onR.method())) {
+                error("JSONP works only with GET transport method", e);
+            }
+            String dataMirror = findDataSpecified(e, onR);
+            if ("PUT".equals(onR.method()) && dataMirror == null) {
+                error("PUT method needs to specify a data() class", e);
+                return false;
+            }
+            if ("POST".equals(onR.method()) && dataMirror == null) {
+                error("POST method needs to specify a data() class", e);
+                return false;
+            }
+            if (e.getParameters().size() < 2) {
+                error("@OnReceive method needs at least two parameters", e);
+            }
+            final boolean isWebSocket = "WebSocket".equals(onR.method());
+            if (isWebSocket && dataMirror == null) {
+                error("WebSocket method needs to specify a data() class", e);
+            }
+            int expectsList = 0;
+            List<String> args = new ArrayList<String>();
+            List<String> params = new ArrayList<String>();
+            // first argument is model class
+            {
+                TypeMirror type = e.getParameters().get(0).asType();
+                CharSequence simpleName;
+                if (type.getKind() == TypeKind.DECLARED) {
+                    simpleName = ((DeclaredType) type).asElement().getSimpleName();
+                } else {
+                    simpleName = type.toString();
+                }
+                if (simpleName.toString().equals(className)) {
+                    args.add("model");
+                } else {
+                    error("First parameter needs to be " + className, e);
+                    return false;
+                }
+            }
+
+            String modelClass;
+            {
+                final Types tu = processingEnv.getTypeUtils();
+                TypeMirror type = e.getParameters().get(1).asType();
+                TypeMirror modelType = null;
+                TypeMirror ert = tu.erasure(type);
+
+                if (isModel(type)) {
+                    modelType = type;
+                } else if (type.getKind() == TypeKind.ARRAY) {
+                    modelType = ((ArrayType)type).getComponentType();
+                    expectsList = 1;
+                } else if ("java.util.List".equals(fqn(ert, e))) {
+                    List<? extends TypeMirror> typeArgs = ((DeclaredType)type).getTypeArguments();
+                    if (typeArgs.size() == 1) {
+                        modelType = typeArgs.get(0);
+                        expectsList = 2;
+                    }
+                } else if (type.toString().equals("java.lang.String")) {
+                    modelType = type;
+                }
+                if (modelType == null) {
+                    error("Second arguments needs to be a model, String or array or List of models", e);
+                    return false;
+                }
+                modelClass = modelType.toString();
+                if (expectsList == 1) {
+                    args.add("arr");
+                } else if (expectsList == 2) {
+                    args.add("java.util.Arrays.asList(arr)");
+                } else {
+                    args.add("arr[0]");
+                }
+            }
+            String n = e.getSimpleName().toString();
+            if (isWebSocket) {
+                body.append("  /** Performs WebSocket communication. Call with <code>null</code> data parameter\n");
+                body.append("  * to open the connection (even if not required). Call with non-null data to\n");
+                body.append("  * send messages to server. Call again with <code>null</code> data to close the socket.\n");
+                body.append("  */\n");
+                if (onR.headers().length > 0) {
+                    error("WebSocket spec does not support headers", e);
+                }
+            }
+            body.append("  public void ").append(n).append("(");
+            StringBuilder urlBefore = new StringBuilder();
+            StringBuilder urlAfter = new StringBuilder();
+            StringBuilder headers = new StringBuilder();
+            String jsonpVarName = null;
+            {
+                String sep = "";
+                boolean skipJSONP = onR.jsonp().isEmpty();
+                Set<String> receiveParams = new LinkedHashSet<String>();
+                findParamNames(receiveParams, e, onR.url(), onR.jsonp(), urlBefore, urlAfter);
+                for (String headerLine : onR.headers()) {
+                    if (headerLine.contains("\r") || headerLine.contains("\n")) {
+                        error("Header line cannot contain line separator", e);
+                    }
+                    findParamNames(receiveParams, e, headerLine, null, headers);
+                    headers.append("+ \"\\r\\n\" +\n");
+                }
+                if (headers.length() > 0) {
+                    headers.append("\"\"");
+                }
+                for (String p : receiveParams) {
+                    if (!skipJSONP && p.equals(onR.jsonp())) {
+                        skipJSONP = true;
+                        jsonpVarName = p;
+                        continue;
+                    }
+                    body.append(sep);
+                    body.append("String ").append(p);
+                    sep = ", ";
+                }
+                if (!skipJSONP) {
+                    error(
+                        "Name of jsonp attribute ('" + onR.jsonp() +
+                        "') is not used in url attribute '" + onR.url() + "'", e
+                    );
+                }
+                if (dataMirror != null) {
+                    body.append(sep).append(dataMirror.toString()).append(" data");
+                }
+                for (int i = 2; i < e.getParameters().size(); i++) {
+                    if (isWebSocket) {
+                        error("@OnReceive(method=\"WebSocket\") can only have two arguments", e);
+                        ok = false;
+                    }
+
+                    VariableElement ve = e.getParameters().get(i);
+                    body.append(sep).append(ve.asType().toString()).append(" ").append(ve.getSimpleName());
+                    final String tp = ve.asType().toString();
+                    String btn = findBoxedType(tp);
+                    if (btn == null) {
+                        btn = tp;
+                    }
+                    args.add("(" + btn + ")params[" + (i - 2) + "]");
+                    params.add(ve.getSimpleName().toString());
+                    sep = ", ";
+                }
+            }
+            body.append(") {\n");
+            boolean webSocket = onR.method().equals("WebSocket");
+            if (webSocket) {
+                if (generateWSReceiveBody(index++, body, inType, onR, e, clazz, className, expectsList != 0, modelClass, n, args, params, urlBefore, jsonpVarName, urlAfter, dataMirror, headers)) {
+                    return false;
+                }
+                body.append("  }\n");
+                body.append("  private Object ws_" + e.getSimpleName() + ";\n");
+            } else {
+                if (generateJSONReceiveBody(index++, body, inType, onR, e, clazz, className, expectsList != 0, modelClass, n, args, params, urlBefore, jsonpVarName, urlAfter, dataMirror, headers)) {
+                    ok = false;
+                }
+                body.append("  }\n");
+            }
+        }
+        inType.append("    }\n");
+        inType.append("    throw new UnsupportedOperationException(\"index: \" + index + \" type: \" + type);\n");
+        inType.append("  }\n");
+        return ok;
+    }
+
+    private boolean generateJSONReceiveBody(int index, StringWriter method, StringBuilder body, OnReceive onR, ExecutableElement e, Element clazz, String className, boolean expectsList, String modelClass, String n, List<String> args, List<String> params, StringBuilder urlBefore, String jsonpVarName, StringBuilder urlAfter, String dataMirror, StringBuilder headers) {
+        boolean error = false;
+        body.append(
+            "    case " + index + ": {\n" +
+            "      if (type == 2) { /* on error */\n" +
+            "        Exception ex = (Exception)data;\n"
+            );
+        if (onR.onError().isEmpty()) {
+            body.append(
+                "        ex.printStackTrace();\n"
+                );
+        } else {
+            int errorParamsLength = findOnError(e, ((TypeElement)clazz), onR.onError(), className);
+            error = errorParamsLength < 0;
+            body.append("        ").append(clazz.getSimpleName()).append(".").append(onR.onError()).append("(");
+            body.append("model, ex");
+            for (int i = 2; i < errorParamsLength; i++) {
+                String arg = args.get(i);
+                body.append(", ");
+                if (arg.startsWith("arr") || arg.startsWith("java.util.Array")) {
+                    body.append("null");
+                } else {
+                    body.append(arg);
+                }
+            }
+            body.append(");\n");
+        }
+        body.append(
+            "        return;\n" +
+            "      } else if (type == 1) {\n" +
+            "        Object[] ev = (Object[])data;\n"
+            );
+        if (expectsList) {
+            body.append(
+                "        " + modelClass + "[] arr = new " + modelClass + "[ev.length];\n"
+                );
+        } else {
+            body.append(
+                "        " + modelClass + "[] arr = { null };\n"
+                );
+        }
+        body.append(
+            "        TYPE.copyJSON(model.proto.getContext(), ev, " + modelClass + ".class, arr);\n"
+        );
+        {
+            body.append("        ").append(clazz.getSimpleName()).append(".").append(n).append("(");
+            String sep = "";
+            for (String arg : args) {
+                body.append(sep);
+                body.append(arg);
+                sep = ", ";
+            }
+            body.append(");\n");
+        }
+        body.append(
+            "        return;\n" +
+            "      }\n" +
+            "    }\n"
+            );
+        method.append("    proto.loadJSONWithHeaders(" + index + ",\n        ");
+        method.append(headers.length() == 0 ? "null" : headers).append(",\n        ");
+        method.append(urlBefore).append(", ");
+        if (jsonpVarName != null) {
+            method.append(urlAfter);
+        } else {
+            method.append("null");
+        }
+        if (!"GET".equals(onR.method()) || dataMirror != null) {
+            method.append(", \"").append(onR.method()).append('"');
+            if (dataMirror != null) {
+                method.append(", data");
+            } else {
+                method.append(", null");
+            }
+        } else {
+            method.append(", null, null");
+        }
+        for (String a : params) {
+            method.append(", ").append(a);
+        }
+        method.append(");\n");
+        return error;
+    }
+
+    private boolean generateWSReceiveBody(int index, StringWriter method, StringBuilder body, OnReceive onR, ExecutableElement e, Element clazz, String className, boolean expectsList, String modelClass, String n, List<String> args, List<String> params, StringBuilder urlBefore, String jsonpVarName, StringBuilder urlAfter, String dataMirror, StringBuilder headers) {
+        body.append(
+            "    case " + index + ": {\n" +
+            "      if (type == 0) { /* on open */\n" +
+            "        ").append(inPckName(clazz, true)).append(".").append(n).append("(");
+        {
+            String sep = "";
+            for (String arg : args) {
+                body.append(sep);
+                if (arg.startsWith("arr") || arg.startsWith("java.util.Array")) {
+                    body.append("null");
+                } else {
+                    body.append(arg);
+                }
+                sep = ", ";
+            }
+        }
+        body.append(");\n");
+        body.append(
+            "        return;\n" +
+            "      } else if (type == 2) { /* on error */\n" +
+            "        Exception value = (Exception)data;\n"
+            );
+        if (onR.onError().isEmpty()) {
+            body.append(
+                "        value.printStackTrace();\n"
+                );
+        } else {
+            int errorParamsLength = findOnError(e, ((TypeElement)clazz), onR.onError(), className);
+            if (errorParamsLength < 0) {
+                return true;
+            }
+            body.append("        ").append(inPckName(clazz, true)).append(".").append(onR.onError()).append("(");
+            body.append("model, value");
+            for (int i = 2; i < errorParamsLength; i++) {
+                String arg = args.get(i);
+                body.append(", ");
+                if (arg.startsWith("arr") || arg.startsWith("java.util.Array")) {
+                    body.append("null");
+                } else {
+                    body.append(arg);
+                }
+            }
+            body.append(");\n");
+        }
+        body.append(
+            "        return;\n" +
+            "      } else if (type == 1) {\n" +
+            "        Object[] ev = (Object[])data;\n"
+        );
+        if (expectsList) {
+            body.append(
+                "        " + modelClass + "[] arr = new " + modelClass + "[ev.length];\n"
+                );
+        } else {
+            body.append(
+                "        " + modelClass + "[] arr = { null };\n"
+                );
+        }
+        body.append(
+            "        TYPE.copyJSON(model.proto.getContext(), ev, " + modelClass + ".class, arr);\n"
+        );
+        {
+            body.append("        ").append(inPckName(clazz, true)).append(".").append(n).append("(");
+            String sep = "";
+            for (String arg : args) {
+                body.append(sep);
+                body.append(arg);
+                sep = ", ";
+            }
+            body.append(");\n");
+        }
+        body.append(
+            "        return;\n" +
+            "      }"
+        );
+        if (!onR.onError().isEmpty()) {
+            body.append(" else if (type == 3) { /* on close */\n");
+            body.append("        ").append(inPckName(clazz, true)).append(".").append(onR.onError()).append("(");
+            body.append("model, null);\n");
+            body.append(
+                "        return;" +
+                "      }"
+            );
+        }
+        body.append("\n");
+        body.append("    }\n");
+        method.append("    if (this.ws_").append(e.getSimpleName()).append(" == null) {\n");
+        method.append("      this.ws_").append(e.getSimpleName());
+        method.append("= proto.wsOpen(" + index + ", ");
+        method.append(urlBefore).append(", data);\n");
+        method.append("    } else {\n");
+        method.append("      proto.wsSend(this.ws_").append(e.getSimpleName()).append(", ").append(urlBefore).append(", data");
+        for (String a : params) {
+            method.append(", ").append(a);
+        }
+        method.append(");\n");
+        method.append("    }\n");
+        return false;
+    }
+
+    private CharSequence wrapParams(
+        ExecutableElement ee, String id, String className, String classRef, String evName, String dataName
+    ) {
+        TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType();
+        StringBuilder params = new StringBuilder();
+        boolean first = true;
+        for (VariableElement ve : ee.getParameters()) {
+            if (!first) {
+                params.append(", ");
+            }
+            first = false;
+            String toCall = null;
+            String toFinish = null;
+            boolean addNull = true;
+            if (ve.asType() == stringType) {
+                if (ve.getSimpleName().contentEquals("id")) {
+                    params.append('"').append(id).append('"');
+                    continue;
+                }
+                toCall = classRef + ".proto.toString(";
+            }
+            if (ve.asType().getKind() == TypeKind.DOUBLE) {
+                toCall = classRef + ".proto.toNumber(";
+                toFinish = ".doubleValue()";
+            }
+            if (ve.asType().getKind() == TypeKind.FLOAT) {
+                toCall = classRef + ".proto.toNumber(";
+                toFinish = ".floatValue()";
+            }
+            if (ve.asType().getKind() == TypeKind.INT) {
+                toCall = classRef + ".proto.toNumber(";
+                toFinish = ".intValue()";
+            }
+            if (ve.asType().getKind() == TypeKind.BYTE) {
+                toCall = classRef + ".proto.toNumber(";
+                toFinish = ".byteValue()";
+            }
+            if (ve.asType().getKind() == TypeKind.SHORT) {
+                toCall = classRef + ".proto.toNumber(";
+                toFinish = ".shortValue()";
+            }
+            if (ve.asType().getKind() == TypeKind.LONG) {
+                toCall = classRef + ".proto.toNumber(";
+                toFinish = ".longValue()";
+            }
+            if (ve.asType().getKind() == TypeKind.BOOLEAN) {
+                toCall = "\"true\".equals(" + classRef + ".proto.toString(";
+                toFinish = ")";
+            }
+            if (ve.asType().getKind() == TypeKind.CHAR) {
+                toCall = "(char)" + classRef + ".proto.toNumber(";
+                toFinish = ".intValue()";
+            }
+            if (dataName != null && ve.getSimpleName().contentEquals(dataName) && isModel(ve.asType())) {
+                toCall = classRef + ".proto.toModel(" + ve.asType() + ".class, ";
+                addNull = false;
+            }
+
+            if (toCall != null) {
+                params.append(toCall);
+                if (dataName != null && ve.getSimpleName().contentEquals(dataName)) {
+                    params.append(dataName);
+                    if (addNull) {
+                        params.append(", null");
+                    }
+                } else {
+                    if (evName == null) {
+                        final StringBuilder sb = new StringBuilder();
+                        sb.append("Unexpected string parameter name.");
+                        if (dataName != null) {
+                            sb.append(" Try \"").append(dataName).append("\"");
+                        }
+                        error(sb.toString(), ee);
+                    }
+                    params.append(evName);
+                    params.append(", \"");
+                    params.append(ve.getSimpleName().toString());
+                    params.append("\"");
+                }
+                params.append(")");
+                if (toFinish != null) {
+                    params.append(toFinish);
+                }
+                continue;
+            }
+            String rn = fqn(ve.asType(), ee);
+            int last = rn.lastIndexOf('.');
+            if (last >= 0) {
+                rn = rn.substring(last + 1);
+            }
+            if (rn.equals(className)) {
+                params.append(classRef);
+                continue;
+            }
+            StringBuilder err = new StringBuilder();
+            err.append("Argument ").
+                append(ve.getSimpleName()).
+                append(" is not valid. The annotated method can only accept ").
+                append(className).
+                append(" argument");
+            if (dataName != null) {
+                err.append(" or argument named '").append(dataName).append("'");
+            }
+            err.append(".");
+            error(err.toString(), ee);
+        }
+        return params;
+    }
+
+
+    private CharSequence wrapPropName(
+        ExecutableElement ee, String className, String propName, String propValue
+    ) {
+        TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType();
+        StringBuilder params = new StringBuilder();
+        boolean first = true;
+        for (VariableElement ve : ee.getParameters()) {
+            if (!first) {
+                params.append(", ");
+            }
+            first = false;
+            if (ve.asType() == stringType) {
+                if (propName != null && ve.getSimpleName().contentEquals(propName)) {
+                    params.append('"').append(propValue).append('"');
+                } else {
+                    error("Unexpected string parameter name. Try \"" + propName + "\".", ee);
+                }
+                continue;
+            }
+            String rn = fqn(ve.asType(), ee);
+            int last = rn.lastIndexOf('.');
+            if (last >= 0) {
+                rn = rn.substring(last + 1);
+            }
+            if (rn.equals(className)) {
+                params.append("model");
+                continue;
+            }
+            error(
+                "@OnPrprtChange method can only accept String or " + className + " arguments",
+                ee);
+        }
+        return params;
+    }
+
+    private boolean isModel(TypeMirror tm) {
+        if (tm.getKind() == TypeKind.ERROR) {
+            return true;
+        }
+        final Element e = processingEnv.getTypeUtils().asElement(tm);
+        if (e == null) {
+            return false;
+        }
+        for (Element ch : e.getEnclosedElements()) {
+            if (ch.getKind() == ElementKind.METHOD) {
+                ExecutableElement ee = (ExecutableElement)ch;
+                if (ee.getParameters().isEmpty() && ee.getSimpleName().contentEquals("modelFor")) {
+                    return true;
+                }
+            }
+        }
+        return models.values().contains(e.getSimpleName().toString());
+    }
+
+    private void writeToString(Prprt[] props, Writer w) throws IOException {
+        w.write("  public String toString() {\n");
+        w.write("    StringBuilder sb = new StringBuilder();\n");
+        w.write("    sb.append('{');\n");
+        String sep = "";
+        for (Prprt p : props) {
+            w.write(sep);
+            w.append("    sb.append('\"').append(\"" + p.name() + "\")");
+                w.append(".append('\"').append(\":\");\n");
+            String tn = typeName(p);
+            String[] gs = toGetSet(p.name(), tn, p.array());
+            boolean isModel[] = { false };
+            boolean isEnum[] = { false };
+            boolean isPrimitive[] = { false };
+            checkType(p, isModel, isEnum, isPrimitive);
+            if (isModel[0]) {
+                w.append("    sb.append(TYPE.toJSON(thisToNull(this.prop_");
+                w.append(p.name()).append(")));\n");
+            } else {
+                w.append("    sb.append(TYPE.toJSON(");
+                w.append(gs[0]).append("()));\n");
+            }
+            sep =    "    sb.append(',');\n";
+        }
+        w.write("    sb.append('}');\n");
+        w.write("    return sb.toString();\n");
+        w.write("  }\n");
+    }
+    private void writeClone(String className, Prprt[] props, Writer w) throws IOException {
+        w.write("  public " + className + " clone() {\n");
+        w.write("    return clone(proto.getContext());\n");
+        w.write("  }\n");
+        w.write("  private " + className + " clone(net.java.html.BrwsrCtx ctx) {\n");
+        w.write("    " + className + " ret = new " + className + "(ctx);\n");
+        for (Prprt p : props) {
+            String tn = typeName(p);
+            String[] gs = toGetSet(p.name(), tn, p.array());
+            if (!p.array()) {
+                boolean isModel[] = { false };
+                boolean isEnum[] = { false };
+                boolean isPrimitive[] = { false };
+                checkType(p, isModel, isEnum, isPrimitive);
+                if (!isModel[0]) {
+                    w.write("    ret.prop_" + p.name() + " = " + gs[0] + "();\n");
+                    continue;
+                }
+                w.write("    ret.prop_" + p.name() + " =  prop_" + p.name() + " == null ? null : prop_" + p.name() + " == this ? ret : " + gs[0] + "().clone();\n");
+            } else {
+                w.write("    proto.cloneList(ret." + gs[0] + "(), ctx, prop_" + p.name() + ");\n");
+            }
+        }
+
+        w.write("    return ret;\n");
+        w.write("  }\n");
+    }
+
+    private String inPckName(Element e, boolean preferInstance) {
+        if (preferInstance && e.getAnnotation(Model.class).instance()) {
+            return "model.instance";
+        }
+        StringBuilder sb = new StringBuilder();
+        while (e.getKind() != ElementKind.PACKAGE) {
+            if (sb.length() == 0) {
+                sb.append(e.getSimpleName());
+            } else {
+                sb.insert(0, '.');
+                sb.insert(0, e.getSimpleName());
+            }
+            e = e.getEnclosingElement();
+        }
+        return sb.toString();
+    }
+
+    private String fqn(TypeMirror pt, Element relative) {
+        if (pt.getKind() == TypeKind.ERROR) {
+            final Elements eu = processingEnv.getElementUtils();
+            PackageElement pckg = eu.getPackageOf(relative);
+            return pckg.getQualifiedName() + "." + pt.toString();
+        }
+        return pt.toString();
+    }
+
+    private String checkType(Prprt p, boolean[] isModel, boolean[] isEnum, boolean[] isPrimitive) {
+        TypeMirror tm;
+        try {
+            String ret = p.typeName(processingEnv);
+            TypeElement e = processingEnv.getElementUtils().getTypeElement(ret);
+            if (e == null) {
+                isModel[0] = true;
+                isEnum[0] = false;
+                isPrimitive[0] = false;
+                return ret;
+            }
+            tm = e.asType();
+        } catch (MirroredTypeException ex) {
+            tm = ex.getTypeMirror();
+        }
+        tm = processingEnv.getTypeUtils().erasure(tm);
+        if (isPrimitive[0] = tm.getKind().isPrimitive()) {
+            isEnum[0] = false;
+            isModel[0] = false;
+            return tm.toString();
+        }
+        final Element e = processingEnv.getTypeUtils().asElement(tm);
+        if (e.getKind() == ElementKind.CLASS && tm.getKind() == TypeKind.ERROR) {
+            isModel[0] = true;
+            isEnum[0] = false;
+            return e.getSimpleName().toString();
+        }
+
+        final Model m = e == null ? null : e.getAnnotation(Model.class);
+        String ret;
+        if (m != null) {
+            ret = findPkgName(e) + '.' + m.className();
+            isModel[0] = true;
+            models.put(e, m.className());
+        } else if (findModelForMthd(e)) {
+            ret = ((TypeElement)e).getQualifiedName().toString();
+            isModel[0] = true;
+        } else {
+            ret = tm.toString();
+        }
+        TypeMirror enm = processingEnv.getElementUtils().getTypeElement("java.lang.Enum").asType();
+        enm = processingEnv.getTypeUtils().erasure(enm);
+        isEnum[0] = processingEnv.getTypeUtils().isSubtype(tm, enm);
+        return ret;
+    }
+
+    private static boolean findModelForMthd(Element clazz) {
+        if (clazz == null) {
+            return false;
+        }
+        for (Element e : clazz.getEnclosedElements()) {
+            if (e.getKind() == ElementKind.METHOD) {
+                ExecutableElement ee = (ExecutableElement)e;
+                if (
+                    ee.getSimpleName().contentEquals("modelFor") &&
+                    ee.getParameters().isEmpty()
+                ) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private void findParamNames(
+        Set<String> params, Element e, String url, String jsonParam, StringBuilder... both
+    ) {
+        int wasJSON = 0;
+
+        for (int pos = 0; ;) {
+            int next = url.indexOf('{', pos);
+            if (next == -1) {
+                both[wasJSON].append('"')
+                    .append(url.substring(pos).replace("\"", "\\\""))
+                    .append('"');
+                return;
+            }
+            int close = url.indexOf('}', next);
+            if (close == -1) {
+                error("Unbalanced '{' and '}' in " + url, e);
+                return;
+            }
+            final String paramName = url.substring(next + 1, close);
+            params.add(paramName);
+            if (paramName.equals(jsonParam) && !jsonParam.isEmpty()) {
+                both[wasJSON].append('"')
+                    .append(url.substring(pos, next).replace("\"", "\\\""))
+                    .append('"');
+                wasJSON = 1;
+            } else {
+                both[wasJSON].append('"')
+                    .append(url.substring(pos, next).replace("\"", "\\\""))
+                    .append("\" + ").append(paramName).append(" + ");
+            }
+            pos = close + 1;
+        }
+    }
+
+    private static Prprt findPrprt(Prprt[] properties, String propName) {
+        for (Prprt p : properties) {
+            if (propName.equals(p.name())) {
+                return p;
+            }
+        }
+        return null;
+    }
+
+    private boolean isPrimitive(String type) {
+        return
+            "int".equals(type) ||
+            "double".equals(type) ||
+            "long".equals(type) ||
+            "short".equals(type) ||
+            "byte".equals(type) ||
+            "char".equals(type) ||
+            "boolean".equals(type) ||
+            "float".equals(type);
+    }
+
+    private static Collection<String> findDerivedFrom(Map<String, Collection<String[]>> propsDeps, String derivedProp) {
+        Set<String> names = new HashSet<String>();
+        for (Map.Entry<String, Collection<String[]>> e : propsDeps.entrySet()) {
+            for (String[] pair : e.getValue()) {
+                if (pair[0].equals(derivedProp)) {
+                    names.add(e.getKey());
+                    break;
+                }
+            }
+        }
+        return names;
+    }
+
+    private Prprt[] createProps(Element e, Property[] arr) {
+        Prprt[] ret = Prprt.wrap(processingEnv, e, arr);
+        Prprt[] prev = verify.put(e, ret);
+        if (prev != null) {
+            error("Two sets of properties for ", e);
+        }
+        return ret;
+    }
+
+    private String findDataSpecified(ExecutableElement e, OnReceive onR) {
+        try {
+            return onR.data().getName();
+        } catch (MirroredTypeException ex) {
+            final TypeMirror tm = ex.getTypeMirror();
+            String name;
+            final Element te = processingEnv.getTypeUtils().asElement(tm);
+            if (te.getKind() == ElementKind.CLASS && tm.getKind() == TypeKind.ERROR) {
+                name = te.getSimpleName().toString();
+            } else {
+                name = tm.toString();
+            }
+            return "java.lang.Object".equals(name) ? null : name;
+        } catch (Exception ex) {
+            // fallback
+        }
+
+        AnnotationMirror found = null;
+        for (AnnotationMirror

<TRUNCATED>


[16/24] incubator-netbeans-html4j git commit: [INFRA-15006] Initial donation of HTML/Java NetBeans API. Equivalent to the content of html4j-donation-review.zip donated as part of ApacheNetBeansDonation1.zip with SHA256 being 7f2ca0f61953a190613c9a0fbcc1b

Posted by jt...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/equinox-agentclass-hook/src/main/resources/hookconfigurators.properties
----------------------------------------------------------------------
diff --git a/equinox-agentclass-hook/src/main/resources/hookconfigurators.properties b/equinox-agentclass-hook/src/main/resources/hookconfigurators.properties
new file mode 100644
index 0000000..d9fe69e
--- /dev/null
+++ b/equinox-agentclass-hook/src/main/resources/hookconfigurators.properties
@@ -0,0 +1,44 @@
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+#
+# Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+# Other names may be trademarks of their respective owners.
+#
+# The contents of this file are subject to the terms of either the GNU
+# General Public License Version 2 only ("GPL") or the Common
+# Development and Distribution License("CDDL") (collectively, the
+# "License"). You may not use this file except in compliance with the
+# License. You can obtain a copy of the License at
+# http://www.netbeans.org/cddl-gplv2.html
+# or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+# specific language governing permissions and limitations under the
+# License.  When distributing the software, include this License Header
+# Notice in each file and include the License file at
+# nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle in the GPL Version 2 section of the License file that
+# accompanied this code. If applicable, add the following below the
+# License Header, with the fields enclosed by brackets [] replaced by
+# your own identifying information:
+# "Portions Copyrighted [year] [name of copyright owner]"
+#
+# Contributor(s):
+#
+# The Original Software is NetBeans. The Initial Developer of the Original
+# Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+#
+# If you wish your version of this file to be governed by only the CDDL
+# or only the GPL Version 2, indicate your decision by adding
+# "[Contributor] elects to include this software in this distribution
+# under the [CDDL or GPL Version 2] license." If you do not indicate a
+# single choice of license, a recipient has the option to distribute
+# your version of this file under either the CDDL, the GPL Version 2 or
+# to extend the choice of license to its licensees as provided above.
+# However, if you add GPL Version 2 code and therefore, elected the GPL
+# Version 2 license, then the option applies only if the new code is
+# made subject to such option by the copyright holder.
+#
+
+hook.configurators=org.netbeans.html.equinox.agentclass.AgentHook

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/geo/pom.xml
----------------------------------------------------------------------
diff --git a/geo/pom.xml b/geo/pom.xml
new file mode 100644
index 0000000..741c6d3
--- /dev/null
+++ b/geo/pom.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2016 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<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">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.netbeans.html</groupId>
+    <artifactId>pom</artifactId>
+    <version>2.0-SNAPSHOT</version>
+  </parent>
+  <groupId>org.netbeans.html</groupId>
+  <artifactId>net.java.html.geo</artifactId>
+  <version>2.0-SNAPSHOT</version>
+  <packaging>bundle</packaging>
+  <name>Geolocation API</name>
+  <url>http://maven.apache.org</url>
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <publicPackages>net.java.html.geo,org.netbeans.html.geo.spi</publicPackages>
+  </properties>
+  <build>
+      <plugins>
+          <plugin>
+              <groupId>org.apache.felix</groupId>
+              <artifactId>maven-bundle-plugin</artifactId>
+          </plugin>
+          <plugin>
+              <groupId>org.netbeans.html</groupId>
+              <artifactId>html4j-maven-plugin</artifactId>
+          </plugin>
+      </plugins>
+  </build>
+  <dependencies>
+      <dependency>
+          <groupId>org.testng</groupId>
+          <artifactId>testng</artifactId>
+      </dependency>
+    <dependency>
+      <groupId>org.netbeans.api</groupId>
+      <artifactId>org-openide-util-lookup</artifactId>
+      <type>jar</type>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.netbeans.html</groupId>
+      <artifactId>net.java.html.boot</artifactId>
+      <version>${project.version}</version>
+      <type>jar</type>
+    </dependency>
+  </dependencies>
+    <description>Find out where your Java program running in an HTML page is!</description>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/geo/src/main/java/net/java/html/geo/OnLocation.java
----------------------------------------------------------------------
diff --git a/geo/src/main/java/net/java/html/geo/OnLocation.java b/geo/src/main/java/net/java/html/geo/OnLocation.java
new file mode 100644
index 0000000..07dc8a6
--- /dev/null
+++ b/geo/src/main/java/net/java/html/geo/OnLocation.java
@@ -0,0 +1,96 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.geo;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** Generates a handle which can configure, {@link Position.Handle#start() start}
+ * and {@link Position.Handle#stop() stop} requests for obtaining current
+ * location of the application/device/user. Put the {@link OnLocation} annotation
+ * on top of a (non-private) method in your class which takes one argument
+ * {@link Position}. Based on name of your method (unless a class name is
+ * directly specified via {@link #className()} attribute) a handle class is
+ * generated. For example if your method name is <code>callMeWhenYouKnowWhereYouAre</code>
+ * a package private class <code>CallMeWhenYouKnowWhereYouAreHandle</code> will
+ * be generated in the same package. One can use its <code>createQuery</code>
+ * and <code>createWatch</code> static method to get one time/repeated time
+ * instance of a {@link Position.Handle handle}. After configuring the
+ * {@link Position.Handle handle} via its setter methods, one can 
+ * {@link Position.Handle#start() start} the location request.
+ * <p>
+ * In case something goes wrong a method in the same class named {@link #onError()}
+ * can be specified (should take one {@link Exception} parameter). 
+ * <p>
+ * The overall behavior of the system mimics <a href="http://www.w3.org/TR/geolocation-API/">
+ * W3C's Geolocation API</a>.
+ *
+ * @author Jaroslav Tulach
+ */
+@Retention(RetentionPolicy.SOURCE)
+@Target(ElementType.METHOD)
+public @interface OnLocation {
+    /** Name of the {@link Position.Handle handle} class to generate. By
+     * default the name is derived from the name of annotated method by
+     * capitalizing the first character and appending <code>Handle</code>.
+     * <p>
+     * The generated class contains two static methods: <code>createQuery</code>
+     * and <code>createWatch</code> which take no parameters if this method 
+     * is static or one parameter (of this class) if this method is instance
+     * one. Both static methods return {@link Position.Handle}.
+     * 
+     * @return string suitable for a new class in the same package
+     */
+    public String className() default "";
+    
+    /** Name of a method in this class which should be called in case of 
+     * an error. The method has to be non-private and take {@link Exception} 
+     * parameter. If this method is not specified, the exception is just
+     * printed to console.
+     * 
+     * @return name of method in this class
+     */
+    public String onError() default "";
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/geo/src/main/java/net/java/html/geo/Position.java
----------------------------------------------------------------------
diff --git a/geo/src/main/java/net/java/html/geo/Position.java b/geo/src/main/java/net/java/html/geo/Position.java
new file mode 100644
index 0000000..4f2edb8
--- /dev/null
+++ b/geo/src/main/java/net/java/html/geo/Position.java
@@ -0,0 +1,383 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.geo;
+
+import java.util.Collections;
+import java.util.ServiceLoader;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import net.java.html.BrwsrCtx;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.geo.impl.Accessor;
+import org.netbeans.html.geo.impl.JsGLProvider;
+import org.netbeans.html.geo.spi.GLProvider;
+
+/** Class that represents a geolocation position provided as a callback
+ * to {@link Handle#onLocation(net.java.html.geo.Position)} method. The
+ * class getters mimic closely the structure of the position object as
+ * specified by <a href="http://www.w3.org/TR/geolocation-API/">
+ * W3C's Geolocation API</a>.
+ *
+ * @author Jaroslav Tulach
+ */
+public final class Position {
+    static final Logger LOG = Logger.getLogger(Position.class.getName());
+    private final long timestamp;
+    private final Coordinates coords;
+
+    public Position(long timestamp, Coordinates coords) {
+        this.timestamp = timestamp;
+        this.coords = coords;
+    }
+    
+    /** The actual location of the position.
+     * @return non-null coordinates
+     */
+    public Coordinates getCoords() {
+        return coords;
+    }
+    
+    /** The time when the position has been recorded.
+     * @return time in milliseconds since era (e.g. Jan 1, 1970).
+     */
+    public long getTimestamp() {
+        return timestamp;
+    }
+
+    /** Actual location of a {@link Position}. 
+     *  Mimics closely <a href="http://www.w3.org/TR/geolocation-API/">
+     * W3C's Geolocation API</a>.
+     */
+    public static abstract class Coordinates {
+        protected Coordinates() {
+            if (!getClass().getName().equals("org.netbeans.html.geo.spi.CoordImpl")) {
+                throw new IllegalStateException();
+            }
+        }
+        
+        /**
+         * @return geographic coordinate specified in decimal degrees.
+         */
+        public abstract double getLatitude();
+
+        /**
+         * @return geographic coordinate specified in decimal degrees.
+         */
+        public abstract double getLongitude();
+
+        /**
+         * The accuracy attribute denotes the accuracy level of the latitude 
+         * and longitude coordinates. It is specified in meters. 
+         * The value of the accuracy attribute must be a non-negative number.
+         * 
+         * @return accuracy in meters
+         */
+        public abstract double getAccuracy();
+        
+        /** Denotes the height of the position, specified in meters above the ellipsoid. 
+         * If the implementation cannot provide altitude information, 
+         * the value of this attribute must be null.
+         * @return value in meters, may return null, if the information is not available 
+         */
+        public abstract Double getAltitude();
+        
+        /**  The altitude accuracy is specified in meters. 
+         * If the implementation cannot provide altitude information, 
+         * the value of this attribute must be null. Otherwise, the value 
+         * must be a non-negative real number.
+         * @return value in meters; may return null, if the information is not available 
+         */
+        public abstract Double getAltitudeAccuracy();
+        
+        /** Denotes the direction of travel of the device and 
+         * is specified in degrees 
+         * counting clockwise relative to the true north. 
+         * 
+         * @return value from 0 to 360 - may return <code>null</code>, 
+         *   if the information is not available 
+         */
+        public abstract Double getHeading();
+        
+        /** Denotes the magnitude of the horizontal component of the 
+         * device's current velocity and is specified in meters per second.
+         * 
+         * @return may return null, if the information is not available 
+         */
+        public abstract Double getSpeed();
+    } // end of Coordinates
+
+    /** Rather than subclassing this class directly consider using {@link OnLocation}
+     * annotation. Such annotation will generate a subclass for you automatically
+     * with two static methods <code>createQuery</code> and <code>createWatch</code>
+     * which can be used to obtain instance of this class.
+     */
+    public static abstract class Handle {
+        private final boolean oneTime;
+        private boolean enableHighAccuracy;
+        private long timeout;
+        private long maximumAge;
+        volatile JsH<?> handle;
+
+        /** Creates new instance of this handle.
+         * 
+         * @param oneTime <code>true</code> if the handle represents one time 
+         *   <em>query</em>. <code>false</code> if it represents a <em>watch</em>
+         */
+        protected Handle(boolean oneTime) {
+            super();
+            this.oneTime = oneTime;
+        }
+
+        /** Callback from the implementation when a (new) position has been
+         * received and identified
+         * @param p the position
+         * @throws Throwable if an exception is thrown, it will be logged by the system
+         */
+        protected abstract void onLocation(Position p) throws Throwable;
+
+        /** Callback when an error occurs.
+         * @param ex the exception describing what went wrong
+         * @throws Throwable if an exception is thrown, it will be logged by the system
+         */
+        protected abstract void onError(Exception ex) throws Throwable;
+        
+        /** Check whether the location API is supported.
+         * @return true, if one can call {@link #start}.
+         */
+        public final boolean isSupported() {
+            JsH<?> p = seekProviders(null, null);
+            if (p != null) {
+                p.stop();
+                return true;
+            }
+            return false;
+        }
+
+        /** Turns on high accuracy mode as specified by the 
+         * <a href="http://www.w3.org/TR/2012/PR­geolocation­API­20120510/">
+         * W3C's Geolocation API</a>. By default the mode is disabled.
+         * @param enable <code>true</code> or <code>false</code>
+         */
+        public final void setHighAccuracy(boolean enable) {
+            this.enableHighAccuracy = enable;
+        }
+
+        /** The amount of milliseconds to wait for a result.
+         * By default infinity.
+         * @param timeout time in milliseconds to wait for a result.
+         */
+        public final void setTimeout(long timeout) {
+            this.timeout = timeout;
+        }
+
+        /** Sets maximum age of cached results which are acceptable to be
+         * returned. By default maximum age is set to zero.
+         * @param age time in milliseconds of acceptable cached results
+         */
+        public final void setMaximumAge(long age) {
+            this.maximumAge = age;
+        }
+        
+        /** Initializes the <em>query</em> or <em>watch</em> request(s) and
+         * returns immediately. Has no effect if the query has already been
+         * started. If a problem appears while starting the system,
+         * it is immediately reported via the {@link #onError(java.lang.Exception)}
+         * callback. For example, if the {@link #isSupported()} method
+         * returns <code>false</code> an IllegalStateException is created
+         * and sent to the {@link #onError(java.lang.Exception) callback} method.
+         */
+        public final void start() {
+            if (handle != null) {
+                return;
+            }
+            
+            Exception[] problem = { null };
+            StringBuilder sb = new StringBuilder();
+            sb.append("[");
+            JsH<?> h = seekProviders(sb, problem);
+            sb.append("\n]");
+            try {
+                if (problem[0] != null) {
+                    onError(problem[0]);
+                    return;
+                }
+                if (h == null) {
+                    onError(new IllegalStateException("geolocation API not supported. Among providers: " + sb));
+                }
+                synchronized (this) {
+                    if (handle != null) {
+                        onError(new IllegalStateException("Parallel request"));
+                    }
+                    handle = h;
+                }
+            } catch (Throwable thr) {
+                LOG.log(Level.INFO, "Problems delivering onError report", thr);
+            }
+        }
+
+        private JsH<?> seekProviders(StringBuilder sb, Exception[] problem) {
+            BrwsrCtx ctx = BrwsrCtx.findDefault(getClass());
+            JsH<?> h = seekProviders(Contexts.find(ctx, GLProvider.class), null, sb, problem);
+            if (h == null) {
+                h = seekProviders(null, ServiceLoader.load(GLProvider.class), sb, problem);
+            }
+            if (h == null) {
+                h = seekProviders(new JsGLProvider(), null, sb, problem);
+            }
+            return h;
+        }
+
+        private JsH<?> seekProviders(
+            GLProvider single, Iterable<GLProvider> set,
+            StringBuilder sb, Exception[] problem
+        ) {
+            if (set == null) {
+                if (single == null) {
+                    return null;
+                }
+                set = Collections.singleton(single);
+            }
+            JsH<?> h = null;
+            for (GLProvider<?,?> p : set) {
+                if (sb != null) {
+                    if (sb.length() > 1) {
+                        sb.append(',');
+                    }
+                    sb.append("\n  ").append(p.getClass().getName());
+                }
+                try {
+                    h = createHandle(p);
+                } catch (Exception ex) {
+                    LOG.log(Level.INFO, "Problems when starting " + p.getClass().getName(), ex);
+                    if (problem != null && problem[0] == null) {
+                        problem[0] = ex;
+                    }
+                }
+                if (h != null) {
+                    break;
+                }
+            }
+            return h;
+        }
+
+        /** Stops all pending requests. After this call no further callbacks
+         * can be obtained. Does nothing if no query or watch was in progress.
+         */
+        public final void stop() {
+            JsH h;
+            synchronized (this) {
+                h = handle;
+                if (h == null) {
+                    return;
+                }
+                handle = null;
+            }
+            h.stop();
+        }
+        
+        private <Watch> JsH<Watch> createHandle(GLProvider<?,Watch> p) {
+            JsH<Watch> temp = new JsH<Watch>(p);
+            return temp.watch == null ? null : temp;
+        }
+
+        private final class JsH<Watch> extends Accessor {
+            private final Watch watch;
+            private final GLProvider<?, Watch> provider;
+            
+            public JsH(GLProvider<?, Watch> p) {
+                super(true);
+                this.watch = Accessor.SPI.start(p, this, oneTime, enableHighAccuracy, timeout, maximumAge);
+                this.provider = p;
+            }
+            
+            @Override
+            public void onLocation(Position position) {
+                if (handle != this) {
+                    return;
+                }
+                if (oneTime) {
+                    stop();
+                }
+                try {
+                    Handle.this.onLocation(position);
+                } catch (Throwable ex) {
+                    LOG.log(Level.SEVERE, null, ex);
+                }
+            }
+
+            @Override
+            public void onError(Exception err) {
+                if (handle != this) {
+                    return;
+                }
+                if (oneTime) {
+                    stop();
+                }
+                try {
+                    Handle.this.onError(err);
+                } catch (Throwable ex) {
+                    LOG.log(Level.SEVERE, null, ex);
+                }
+            }
+
+            protected final void stop() {
+                Accessor.SPI.stop(provider, watch);
+            }
+
+            @Override
+            public <Watch> Watch start(
+                GLProvider<?, Watch> p, Accessor peer,
+                boolean oneTime, boolean enableHighAccuracy,
+                long timeout, long maximumAge
+            ) {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public <Watch> void stop(GLProvider<?, Watch> p, Watch w) {
+                throw new UnsupportedOperationException();
+            }
+
+        } // end of JsH
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/geo/src/main/java/net/java/html/geo/doc-files/GeoDuke.png
----------------------------------------------------------------------
diff --git a/geo/src/main/java/net/java/html/geo/doc-files/GeoDuke.png b/geo/src/main/java/net/java/html/geo/doc-files/GeoDuke.png
new file mode 100644
index 0000000..56826a1
Binary files /dev/null and b/geo/src/main/java/net/java/html/geo/doc-files/GeoDuke.png differ

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/geo/src/main/java/net/java/html/geo/package.html
----------------------------------------------------------------------
diff --git a/geo/src/main/java/net/java/html/geo/package.html b/geo/src/main/java/net/java/html/geo/package.html
new file mode 100644
index 0000000..bf9c6ab
--- /dev/null
+++ b/geo/src/main/java/net/java/html/geo/package.html
@@ -0,0 +1,74 @@
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>Geolocation API for Java</title>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <meta name="viewport" content="width=device-width">
+    </head>
+    <body>
+        <span id="geoduke"></span>
+        <div>
+        HTML Geo API for Java provides <a href="OnLocation.html">annotation based way</a> of
+        obtaining geolocation information from a browser or any other 
+        device capable of providing it.
+        The primary way to use this API is to 
+        create a non-private method and annotate it with <a href="OnLocation.html">@OnLocation</a>
+        annotation.
+        </div>
+        <img src="doc-files/GeoDuke.png" alt="Illustrative picture"/>
+        <script type="text/javascript">
+            var s = document.getElementById("geoduke");
+            var i = document.createElement("img");
+            i.width = 120;
+            i.height = 60;
+            i.align = 'right';
+            i.src="doc-files/GeoDuke.png";
+            i.alt="Illustrative picture";
+            s.appendChild(i);
+        </script>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/geo/src/main/java/org/netbeans/html/geo/impl/Accessor.java
----------------------------------------------------------------------
diff --git a/geo/src/main/java/org/netbeans/html/geo/impl/Accessor.java b/geo/src/main/java/org/netbeans/html/geo/impl/Accessor.java
new file mode 100644
index 0000000..e394c95
--- /dev/null
+++ b/geo/src/main/java/org/netbeans/html/geo/impl/Accessor.java
@@ -0,0 +1,76 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.geo.impl;
+
+import net.java.html.geo.Position;
+import org.netbeans.html.geo.spi.GLProvider;
+
+/** Connection between API and SPI parts of the module.
+ *
+ * @author Jaroslav Tulach <jt...@netbeans.org>
+ */
+public abstract class Accessor {
+    public static Accessor SPI;
+    static {
+        JsGLProvider initGLProviderClass = new JsGLProvider();
+    }
+    
+    protected Accessor(boolean api) {
+        if (!api) {
+            assert SPI == null;
+            SPI = this;
+        }
+    }
+    
+    public abstract <Watch> Watch start(
+        GLProvider<?, Watch> p, Accessor peer,
+        boolean oneTime, boolean enableHighAccuracy,
+        long timeout, long maximumAge
+    );
+    
+    public abstract <Watch> void stop(GLProvider<?, Watch> p, Watch w);
+    
+    public abstract void onError(Exception ex);
+
+    public abstract void onLocation(Position position);
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/geo/src/main/java/org/netbeans/html/geo/impl/GeoProcessor.java
----------------------------------------------------------------------
diff --git a/geo/src/main/java/org/netbeans/html/geo/impl/GeoProcessor.java b/geo/src/main/java/org/netbeans/html/geo/impl/GeoProcessor.java
new file mode 100644
index 0000000..ff2635e
--- /dev/null
+++ b/geo/src/main/java/org/netbeans/html/geo/impl/GeoProcessor.java
@@ -0,0 +1,290 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.geo.impl;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.Processor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.annotation.processing.SupportedSourceVersion;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+import javax.tools.Diagnostic;
+import javax.tools.JavaFileObject;
+import net.java.html.geo.OnLocation;
+import net.java.html.geo.Position;
+import net.java.html.geo.Position.Handle;
+import org.openide.util.lookup.ServiceProvider;
+
+/** Annotation processor to generate callbacks from {@link Handle} class.
+ *
+ * @author Jaroslav Tulach
+ */
+@ServiceProvider(service=Processor.class)
+@SupportedSourceVersion(SourceVersion.RELEASE_6)
+@SupportedAnnotationTypes({
+    "net.java.html.geo.OnLocation"
+})
+public final class GeoProcessor extends AbstractProcessor {
+    private static final Logger LOG = Logger.getLogger(GeoProcessor.class.getName());
+    @Override
+    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+        boolean ok = true;
+        for (Element e : roundEnv.getElementsAnnotatedWith(OnLocation.class)) {
+            if (!processLocation(e)) {
+                ok = false;
+            }
+        }
+        return ok;
+    }
+
+    private void error(String msg, Element e) {
+        processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, msg, e);
+    }
+    
+    private boolean processLocation(Element e) {
+        if (e.getKind() != ElementKind.METHOD) {
+            return false;
+        }
+        ExecutableElement me = (ExecutableElement) e;
+        OnLocation ol = e.getAnnotation(OnLocation.class);
+        if (ol == null) {
+            return true;
+        }
+        if (me.getModifiers().contains(Modifier.PRIVATE)) {
+            error("Method annotated by @OnLocation cannot be private", e);
+            return false;
+        }
+        TypeMirror positionClass = processingEnv.getElementUtils().getTypeElement(Position.class.getName()).asType();
+        final List<? extends VariableElement> params = me.getParameters();
+        if (params.size() < 1 || !params.get(0).asType().equals(positionClass)) {
+            error("Method annotated by @OnLocation first argument must be net.java.html.geo.Position!", e);
+            return false;
+        }
+        String className = ol.className();
+        if (className.isEmpty()) {
+            String n = e.getSimpleName().toString();
+            if (n.isEmpty()) {
+                error("Empty method name", e);
+                return false;
+            }
+            final String firstLetter = n.substring(0, 1).toUpperCase(Locale.ENGLISH);
+            className = firstLetter + n.substring(1) + "Handle";
+        }
+        TypeElement te = (TypeElement)e.getEnclosingElement();
+        PackageElement pe = (PackageElement) te.getEnclosingElement();
+        final String pkg = pe.getQualifiedName().toString();
+        final String fqn = pkg + "." + className;
+        final boolean isStatic = me.getModifiers().contains(Modifier.STATIC);
+        String sep;
+        try {
+            JavaFileObject fo = processingEnv.getFiler().createSourceFile(fqn, e);
+            Writer w = fo.openWriter();
+            w.append("package ").append(pkg).append(";\n");
+            w.append("class ").append(className).append(" extends net.java.html.geo.Position.Handle {\n");
+            if (!isStatic) {
+                w.append("  private final ").append(te.getSimpleName()).append(" $i;\n");
+            }
+            for (int i = 1; i < params.size(); i++) {
+                final VariableElement p = params.get(i);
+                w.append("  private final ").append(p.asType().toString()).append(" ").append(p.getSimpleName()).append(";\n");
+            }
+            w.append("  private ").append(className).append("(boolean oneTime");
+            w.append(", ").append(te.getSimpleName()).append(" i");
+            for (int i = 1; i < params.size(); i++) {
+                final VariableElement p = params.get(i);
+                w.append(", ").append(p.asType().toString()).append(" ").append(p.getSimpleName());
+            }
+            w.append(") {\n    super(oneTime);\n");
+            if (!isStatic) {
+                w.append("    this.$i = i;\n");
+            }
+            for (int i = 1; i < params.size(); i++) {
+                final VariableElement p = params.get(i);
+                w.append("  this.").append(p.getSimpleName()).append(" = ").append(p.getSimpleName()).append(";\n");
+            }
+            w.append("}\n");
+            w.append("  static net.java.html.geo.Position.Handle createQuery(");
+            String inst;
+            if (!isStatic) {
+                w.append(te.getSimpleName()).append(" instance");
+                inst = "instance";
+                sep = ", ";
+            } else {
+                inst = "null";
+                sep = "";
+            }
+            for (int i = 1; i < params.size(); i++) {
+                final VariableElement p = params.get(i);
+                w.append(sep).append(p.asType().toString()).append(" ").append(p.getSimpleName());
+                sep = ", ";
+            }
+            w.append(") { return new ").append(className).append("(true, ").append(inst);
+            for (int i = 1; i < params.size(); i++) {
+                final VariableElement p = params.get(i);
+                w.append(", ").append(p.getSimpleName());
+            }
+            w.append("); }\n");
+            w.append("  static net.java.html.geo.Position.Handle createWatch(");
+            if (!isStatic) {
+                w.append(te.getSimpleName()).append(" instance");
+                sep = ", ";
+            } else {
+                sep = "";
+            }
+            for (int i = 1; i < params.size(); i++) {
+                final VariableElement p = params.get(i);
+                w.append(sep).append(p.asType().toString()).append(" ").append(p.getSimpleName());
+            }
+            w.append(") { return new ").append(className).append("(false, ").append(inst);
+            for (int i = 1; i < params.size(); i++) {
+                final VariableElement p = params.get(i);
+                w.append(", ").append(p.getSimpleName());
+            }
+            w.append("); }\n");
+            w.append("  @Override protected void onError(Exception t) throws Throwable {\n");
+            if (ol.onError().isEmpty()) {
+                w.append("    t.printStackTrace();");
+            } else {
+                if (!findOnError(me, te, ol.onError(), isStatic)) {
+                    return false;
+                }
+                if (isStatic) {
+                    w.append("    ").append(te.getSimpleName()).append(".");
+                } else {
+                    w.append("    $i.");
+                }
+                w.append(ol.onError()).append("(t");
+                for (int i = 1; i < params.size(); i++) {
+                    final VariableElement p = params.get(i);
+                    w.append(", ").append(p.getSimpleName());
+                }
+                w.append(");\n");
+            }
+            w.append("  }\n");
+            w.append("  @Override protected void onLocation(net.java.html.geo.Position p) throws Throwable {\n");
+            if (isStatic) {
+                w.append("    ").append(te.getSimpleName()).append(".");
+            } else {
+                w.append("    $i.");
+            }
+            w.append(me.getSimpleName()).append("(p");
+            for (int i = 1; i < params.size(); i++) {
+                final VariableElement p = params.get(i);
+                w.append(", ").append(p.getSimpleName());
+            }
+            w.append(");\n");
+            w.append("  }\n");
+            w.append("}\n");
+            w.close();
+        } catch (IOException ex) {
+            Logger.getLogger(GeoProcessor.class.getName()).log(Level.SEVERE, null, ex);
+            error("Can't write handler class: " + ex.getMessage(), e);
+            return false;
+        }
+        
+        return true;
+    }
+
+    private boolean findOnError(ExecutableElement errElem, TypeElement te, String name, boolean onlyStatic) {
+        String err = null;
+        METHODS: for (Element e : te.getEnclosedElements()) {
+            if (e.getKind() != ElementKind.METHOD) {
+                continue;
+            }
+            if (!e.getSimpleName().contentEquals(name)) {
+                continue;
+            }
+            if (onlyStatic && !e.getModifiers().contains(Modifier.STATIC)) {
+                errElem = (ExecutableElement) e;
+                err = "Would have to be static";
+                continue;
+            }
+            ExecutableElement ee = (ExecutableElement) e;
+            TypeMirror excType = processingEnv.getElementUtils().getTypeElement(Exception.class.getName()).asType();
+            final List<? extends VariableElement> params = ee.getParameters(); 
+            if (params.size() < 1 || 
+                !processingEnv.getTypeUtils().isAssignable(excType, ee.getParameters().get(0).asType())
+            ) {
+                errElem = (ExecutableElement) e;
+                err = "Error method first argument needs to be Exception";
+                continue;
+            }
+            final List<? extends Element> origParams = errElem.getParameters();
+            if (params.size() != origParams.size()) {
+                errElem = (ExecutableElement) e;
+                err = "Error method must have the same parameters as @OnLocation one";
+                continue;
+            }
+            for (int i = 1; i < origParams.size(); i++) {
+                final TypeMirror t1 = params.get(i).asType();
+                final TypeMirror t2 = origParams.get(i).asType();
+                if (!processingEnv.getTypeUtils().isSameType(t1, t2)) {
+                    errElem = (ExecutableElement) e;
+                    err = "Error method must have the same parameters as @OnLocation one";
+                    continue METHODS;
+                }
+            }
+            return true;
+        }
+        if (err == null) {
+            err = "Cannot find " + name + "(Exception) method in this class";
+        }
+        error(err, errElem);
+        return false;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/geo/src/main/java/org/netbeans/html/geo/impl/JsGLProvider.java
----------------------------------------------------------------------
diff --git a/geo/src/main/java/org/netbeans/html/geo/impl/JsGLProvider.java b/geo/src/main/java/org/netbeans/html/geo/impl/JsGLProvider.java
new file mode 100644
index 0000000..f7578a8
--- /dev/null
+++ b/geo/src/main/java/org/netbeans/html/geo/impl/JsGLProvider.java
@@ -0,0 +1,154 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.geo.impl;
+
+import net.java.html.js.JavaScriptBody;
+import org.netbeans.html.geo.spi.GLProvider;
+
+/** Implementation class to deal with browser's <code>navigator.geolocation</code> 
+ * object.
+ *
+ * @author Jaroslav Tulach
+ */
+public final class JsGLProvider extends GLProvider<Object, Long> {
+    public JsGLProvider() {
+    }
+    
+    @JavaScriptBody(args = {}, body = "return !!navigator.geolocation;")
+    private static boolean hasGeolocation() {
+        return false;
+    }
+
+    @JavaScriptBody(
+        args = { "c", "onlyOnce", "enableHighAccuracy", "timeout", "maximumAge" }, 
+        javacall = true, 
+        body = 
+        "var self = this;\n" +
+        "var ok = function (position) {\n" +
+        "  self.@org.netbeans.html.geo.impl.JsGLProvider::onLocation(Ljava/lang/Object;Ljava/lang/Object;)(c, position);\n" +
+        "};\n" +
+        "var fail = function (error) {\n" +
+        "  self.@org.netbeans.html.geo.impl.JsGLProvider::onError(Ljava/lang/Object;Ljava/lang/String;I)(c, error.message, error.code);\n" +
+        "};\n" +
+        "var options = {};\n" +
+        "options.enableHighAccuracy = enableHighAccuracy;\n" +
+        "if (timeout >= 0) options.timeout = timeout;\n" +
+        "if (maximumAge >= 0) options.maximumAge = maximumAge;\n" +
+        "if (onlyOnce) {\n" +
+        "  navigator.geolocation.getCurrentPosition(ok, fail, options);\n" +
+        "  return 0;\n" +
+        "} else {\n" +
+        "  return navigator.geolocation.watchPosition(ok, fail, options);\n" +
+        "}\n"
+    )
+    private long doStart(
+        Query c,
+        boolean onlyOnce, 
+        boolean enableHighAccuracy,
+        long timeout,
+        long maximumAge
+    ) {
+        return -1;
+    }
+    
+    protected void stop(long watch) {
+    }
+
+    @Override
+    public Long start(Query c) {
+        if (!hasGeolocation()) {
+            return null;
+        }
+        return doStart(c, c.isOneTime(), c.isHighAccuracy(), c.getTimeout(), c.getMaximumAge());
+    }
+    
+    final void onLocation(Object c, Object p) {
+        callback((Query)c, timeStamp(p), p, null);
+    }
+    
+    final void onError(Object c, final String msg, int code) {
+        final Exception err = new Exception(msg + " errno: " + code) {
+            @Override
+            public String getLocalizedMessage() {
+                return msg;
+            }
+        };
+        callback((Query)c, 0L, null, err);
+    }
+
+    @Override
+    @JavaScriptBody(args = {"watch"}, body = "navigator.geolocation.clearWatch(watch);")
+    public native void stop(Long watch);
+
+    @JavaScriptBody(args = { "p" }, body = "return p.timestamp;")
+    private static native long timeStamp(Object position);
+
+    @Override
+    @JavaScriptBody(args = { "coords" }, body = "return coords.coords.latitude;")
+    protected native double latitude(Object coords);
+
+    @Override
+    @JavaScriptBody(args = { "coords" }, body = "return coords.coords.longitude;")
+    protected native double longitude(Object coords);
+
+    @Override
+    @JavaScriptBody(args = { "coords" }, body = "return coords.coords.accuracy;")
+    protected native double accuracy(Object coords);
+
+    @Override
+    @JavaScriptBody(args = {"coords"}, body = "return coords.coords.altitude ? coords.coords.altitude : null;")
+    protected native Double altitude(Object coords);
+
+    @Override
+    @JavaScriptBody(args = {"coords"}, body = "return coords.coords.altitudeAccuracy ? coords.coords.altitudeAccuracy : null;")
+    protected native Double altitudeAccuracy(Object coords);
+
+    @Override
+    @JavaScriptBody(args = {"coords"}, body = "return coords.coords.heading ? coords.coords.heading : null;")
+    protected native Double heading(Object coords);
+
+    @Override
+    @JavaScriptBody(args = {"coords"}, body = "return coords.coords.speed ? coords.coords.speed : null;")
+    protected native Double speed(Object coords);
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/geo/src/main/java/org/netbeans/html/geo/spi/CoordImpl.java
----------------------------------------------------------------------
diff --git a/geo/src/main/java/org/netbeans/html/geo/spi/CoordImpl.java b/geo/src/main/java/org/netbeans/html/geo/spi/CoordImpl.java
new file mode 100644
index 0000000..b0f0a14
--- /dev/null
+++ b/geo/src/main/java/org/netbeans/html/geo/spi/CoordImpl.java
@@ -0,0 +1,87 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.geo.spi;
+
+import net.java.html.geo.Position;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+final class CoordImpl<Coords> extends Position.Coordinates {
+    private final Coords data;
+    private final GLProvider<Coords, ?> provider;
+
+    CoordImpl(Coords data, GLProvider<Coords, ?> p) {
+        this.data = data;
+        this.provider = p;
+    }
+
+    @Override public double getLatitude() {
+        return provider.latitude(data);
+    }
+
+    @Override public double getLongitude() {
+        return provider.longitude(data);
+    }
+
+    @Override public double getAccuracy() {
+        return provider.accuracy(data);
+    }
+
+    @Override public Double getAltitude() {
+        return provider.altitude(data);
+    }
+
+    @Override public Double getAltitudeAccuracy() {
+        return provider.altitudeAccuracy(data);
+    }
+
+    @Override public Double getHeading() {
+        return provider.heading(data);
+    }
+
+    @Override public Double getSpeed() {
+        return provider.speed(data);
+    }
+} // end of CoordImpl

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/geo/src/main/java/org/netbeans/html/geo/spi/GLProvider.java
----------------------------------------------------------------------
diff --git a/geo/src/main/java/org/netbeans/html/geo/spi/GLProvider.java b/geo/src/main/java/org/netbeans/html/geo/spi/GLProvider.java
new file mode 100644
index 0000000..6ea1346
--- /dev/null
+++ b/geo/src/main/java/org/netbeans/html/geo/spi/GLProvider.java
@@ -0,0 +1,306 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.geo.spi;
+
+import net.java.html.BrwsrCtx;
+import net.java.html.geo.Position;
+import net.java.html.geo.Position.Handle;
+import net.java.html.geo.Position.Coordinates;
+import org.netbeans.html.context.spi.Contexts.Builder;
+import org.netbeans.html.geo.impl.Accessor;
+import org.openide.util.lookup.ServiceProvider;
+
+/** SPI for those who wish to provide their own way of obtaining geolocation.
+ * Subclass this class, implement its method and register it into the system.
+ * You can either use {@link ServiceProvider} to register globally, or 
+ * one can register into {@link BrwsrCtx} (via 
+ * {@link Builder#register(java.lang.Class, java.lang.Object, int) context builder}).
+ * <p>
+ * There is default system provider (used as a fallback) based on 
+ * <a href="http://www.w3.org/TR/geolocation-API/">
+ * W3C's Geolocation</a> specification - if you are running inside a
+ * browser that supports such standard and you are satisfied with its
+ * behavior, you don't have to register anything.
+ * <p>
+ * The provider serves two purposes: 
+ * <ol>
+ *   <li>
+ *     It handles a geolocation request and creates a "watch" to represent it -
+ *     to do so implement the {@link #start(org.netbeans.html.geo.spi.GLProvider.Query) start} 
+ *     method and the {@link #stop(java.lang.Object) stop} method.
+ *   </li>
+ *   <li>
+ *     Once the location is found, the provider needs to 
+ *     {@link #callback(org.netbeans.html.geo.spi.GLProvider.Query, long, java.lang.Object, java.lang.Exception)  call back}
+ *     with appropriate location information which can be extracted
+ *     later via {@link #latitude(java.lang.Object)} {@link #longitude(java.lang.Object)}, and
+ *     other methods in this that also need to be implemented.
+ *   </li>
+ * </ol>
+ * <p>
+ * The provider is based on a 
+ * <a href="http://wiki.apidesign.org/wiki/Singletonizer" target="_blank">singletonizer</a> 
+ * pattern (applied twice)
+ * and as such one is only required to subclass just the {@link GLProvider} 
+ * and otherwise has freedom choosing what classes to use
+ * to represent coordinates and watches. For example if it is enough to use
+ * an array for coordinates and a long number for a watch, one can do:
+ * <pre>
+ * <b>public final class</b> MyGeoProvider extends {@link GLProvider}&lt;Double[], Long&gt; {
+ *   <em>// somehow implement the methods</em>
+ * }
+ * </pre>
+ *
+ * @author Jaroslav Tulach
+ * @param <Watch> your chosen type to represent one query (one time) or watch (repeated) request -
+ *   this type is used in {@link #start(org.netbeans.html.geo.spi.GLProvider.Query) start}
+ *   and {@link #stop(java.lang.Object) stop} methods.
+ * 
+ * @param <Coords> your chosen type to represent geolocation coordinates -
+ *   use in many methods in this class like {@link #latitude(java.lang.Object)} and
+ *   {@link #longitude(java.lang.Object)}.
+ * 
+ * @since 1.0
+ */
+public abstract class GLProvider<Coords,Watch> {
+    static {
+        Accessor initChannel = new Accessor(false) {
+            @Override
+            public <Watch> Watch start(GLProvider<?, Watch> p, Accessor peer, boolean oneTime, boolean enableHighAccuracy, long timeout, long maximumAge) {
+                return p.start(new Query(peer, oneTime, enableHighAccuracy, timeout, maximumAge));
+            }
+            
+            @Override
+            public <Watch> void stop(GLProvider<?, Watch> p, Watch w) {
+                p.stop(w);
+            }
+            
+            @Override
+            public void onError(Exception ex) {
+                throw new UnsupportedOperationException();
+            }
+            
+            @Override
+            public void onLocation(Position position) {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+    /** Start obtaining geolocation.
+     * When the client {@link Handle#start() requests location} (and
+     * your provider is found) this method should initialize the request or 
+     * return <code>null</code> to give chance to another provider.
+     * 
+     * @param c the query describing the request and
+     *   to {@link #callback(org.netbeans.html.geo.spi.GLProvider.Query, long, java.lang.Object, java.lang.Exception)  use when location is found} -
+     *    keep it, you'll need it later
+     * 
+     * @return an object representing the request (so it can be {@link #stop(java.lang.Object) stopped} later)
+     *   or <code>null</code> if this provider was unable to start the request
+     */
+    protected abstract Watch start(Query c);
+    
+    /** Called when a geolocation request should be stopped.
+     * 
+     * @param watch the watch returned when {@link #start(org.netbeans.html.geo.spi.GLProvider.Query) starting}
+     *   the request
+     */
+    protected abstract void stop(Watch watch);
+
+    /** Invoke this method when your provider obtained requested location.
+     * This single method is used for notification of success (when <code>ex</code>
+     * argument is <code>null</code> and <code>position</code> is provided) or 
+     * a failure (when <code>ex</code> argument is non-<code>null</code>).
+     * A successful requests leads in a call to {@link Handle#onLocation(net.java.html.geo.Position)}
+     * while an error report leads to a call to {@link Handle#onError(java.lang.Exception)}.
+     * The actual call is sent to {@link BrwsrCtx#execute(java.lang.Runnable)} of
+     * context recorded when the {@link Query} was created to guarantee it
+     * happens on the right browser thread - however it may happen "later"
+     * when this method has already finished.
+     * 
+     * @param c the query as provided when {@link #start(org.netbeans.html.geo.spi.GLProvider.Query) starting}
+     *   the request
+     * @param timestamp milliseconds since epoch when the location has been obtained
+     * @param position your own, internal, representation of geolocation
+     *   coordinates - will be passed back to other methods of this class
+     *   like {@link #latitude(java.lang.Object)} and {@link #longitude(java.lang.Object)}.
+     *   Can be <code>null</code> if <code>ex</code> is non-<code>null</code>
+     * @param ex an exception to signal an error - should be <code>null</code>
+     *   when one notifies the successfully obtained <code>position</code>
+     */
+    protected final void callback(
+        final Query c,
+        final long timestamp, final Coords position,
+        final Exception ex
+    ) {
+        c.ctx.execute(new Runnable() {
+            @Override
+            public void run() {
+                if (ex == null) {
+                    c.peer.onLocation(new Position(timestamp, new CoordImpl<Coords>(position, GLProvider.this)));
+                } else {
+                    c.peer.onError(ex);
+                }
+            }
+        });
+    }
+
+    /** Extracts value for {@link Coordinates#getLatitude()}.
+     * @param coords your own internal representation of coordinates.
+     * @return geographic coordinate specified in decimal degrees.
+     */
+    protected abstract double latitude(Coords coords);
+    
+    /** Extracts value for {@link Coordinates#getLatitude()}.
+     * @param coords your own internal representation of coordinates.
+     * @return geographic coordinate specified in decimal degrees.
+     */
+    protected abstract double longitude(Coords coords);
+    
+    /** Extracts value for {@link Coordinates#getLatitude()}.
+     * The accuracy attribute denotes the accuracy level of the latitude 
+     * and longitude coordinates.
+     * 
+     * @param coords your own internal representation of coordinates.
+     * @return accuracy in meters
+     */
+    protected abstract double accuracy(Coords coords);
+    
+    /** Extracts value for {@link Coordinates#getAltitude()}.
+     * Denotes the height of the position, specified in meters above the ellipsoid.
+     * 
+     * @param coords your own internal representation of coordinates.
+     * @return value in meters, may return null, if the information is not available
+     */
+    protected abstract Double altitude(Coords coords);
+    
+    /** Extracts value for {@link Coordinates#getAltitudeAccuracy()} -
+     * the altitude accuracy is specified in meters. 
+     * 
+     * @param coords your own internal representation of coordinates.
+     * @return value in meters; may return null, if the information is not available
+     */
+    protected abstract Double altitudeAccuracy(Coords coords);
+    
+    /** Extracts value for {@link Coordinates#getHeading()}.
+     * Denotes the magnitude of the horizontal component of the 
+     * device's current velocity and is specified in meters per second.
+     * 
+     * @param coords your own internal representation of coordinates.
+     * @return may return null, if the information is not available 
+     */
+    protected abstract Double heading(Coords coords);
+    
+    /** Extracts value for {@link Coordinates#getSpeed()}.
+     * Denotes the magnitude of the horizontal component of the 
+     * device's current velocity and is specified in meters per second.
+     * 
+     * @param coords your own internal representation of coordinates.
+     * @return may return null, if the information is not available
+     */
+    protected abstract Double speed(Coords coords);
+    
+    /** Holds parameters describing the location query and is used by {@link GLProvider} to notify back
+     * results of its findings.
+     */
+    public static final class Query {
+        private final boolean oneTime;
+        private final boolean enableHighAccuracy;
+        private final long timeout;
+        private final long maximumAge;
+        private final BrwsrCtx ctx;
+        final Accessor peer;
+
+        Query(Accessor peer, boolean oneTime, boolean enableHighAccuracy, long timeout, long maximumAge) {
+            this.peer = peer;
+            this.oneTime = oneTime;
+            this.enableHighAccuracy = enableHighAccuracy;
+            this.timeout = timeout;
+            this.maximumAge = maximumAge;
+            ctx = BrwsrCtx.findDefault(Query.class);
+        }
+        
+        /**
+         * Is this one time or repeated request? Mimics value provided in
+         * {@link Handle constructor}.
+         *
+         * @return true if this is one time request, false if the request is
+         * permanent (up until {@link Handle#stop() } is called).
+         */
+        public final boolean isOneTime() {
+            return oneTime;
+        }
+
+        /**
+         * Turns on high accuracy mode as specified by the
+         * <a href="http://www.w3.org/TR/2012/PR­geolocation­API­20120510/">
+         * W3C's Geolocation API</a>. By default the mode is disabled. Mimics
+         * value of {@link Handle#setHighAccuracy(boolean)}.
+         *
+         * @return enable <code>true</code> or <code>false</code>
+         */
+        public final boolean isHighAccuracy() {
+            return this.enableHighAccuracy;
+        }
+
+        /**
+         * The amount of milliseconds to wait for a result. Mimics value of
+         * {@link Handle#setTimeout(long)}.
+         *
+         * @return time in milliseconds to wait for a result.
+         */
+        public final long getTimeout() {
+            return this.timeout;
+        }
+
+        /**
+         * Sets maximum age of cached results which are acceptable to be
+         * returned. Mimics value of {@link Handle#setMaximumAge(long)}.
+         *
+         * @return time in milliseconds of acceptable cached results
+         */
+        public final long getMaximumAge() {
+            return this.maximumAge;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/geo/src/main/java/org/netbeans/html/geo/spi/package.html
----------------------------------------------------------------------
diff --git a/geo/src/main/java/org/netbeans/html/geo/spi/package.html b/geo/src/main/java/org/netbeans/html/geo/spi/package.html
new file mode 100644
index 0000000..6eb717c
--- /dev/null
+++ b/geo/src/main/java/org/netbeans/html/geo/spi/package.html
@@ -0,0 +1,59 @@
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>Geolocation SPI</title>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <meta name="viewport" content="width=device-width">
+    </head>
+    <body>
+        <div>
+            Service provider interfaces for those willing to 
+            {@link org.netbeans.html.geo.spi.GLProvider provide their own way}
+            of obtaining proper geolocation.
+        </div>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/geo/src/test/java/net/java/html/geo/OnLocationTest.java
----------------------------------------------------------------------
diff --git a/geo/src/test/java/net/java/html/geo/OnLocationTest.java b/geo/src/test/java/net/java/html/geo/OnLocationTest.java
new file mode 100644
index 0000000..544d8c4
--- /dev/null
+++ b/geo/src/test/java/net/java/html/geo/OnLocationTest.java
@@ -0,0 +1,137 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.geo;
+
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+/** Testing correctness of the generated code.
+ */
+public class OnLocationTest {
+    static int cnt;
+    static @OnLocation void onLocation(Position p) {
+        assertNotNull(p, "Position object provided");
+        cnt++;
+    }
+
+    @Test public void createOneTimeQueryStatic() {
+        net.java.html.geo.Position.Handle h = OnLocationHandle.createQuery();
+        h.setHighAccuracy(false);
+        h.setTimeout(1000L);
+        h.setMaximumAge(1000L);
+        if (h.isSupported()) h.start();
+        h.stop();
+    }
+    
+    @Test public void onLocationHandleCallback() throws Throwable {
+        net.java.html.geo.Position.Handle h = OnLocationHandle.createQuery();
+        cnt = 0;
+        h.onLocation(new Position(0L, null));
+        assertEquals(cnt, 1, "The callback has been made");
+    }
+
+    @Test public void createRepeatableWatchStatic() {
+        net.java.html.geo.Position.Handle h = OnLocationHandle.createQuery();
+        h.setHighAccuracy(false);
+        h.setTimeout(1000L);
+        h.setMaximumAge(1000L);
+        if (h.isSupported()) h.start();
+        h.stop();
+    }
+
+    int instCnt;
+    Throwable instT;
+    @OnLocation(onError = "someError") void instance(Position p) throws Error {
+        assertNotNull(p, "Some position passed in");
+        instCnt++;
+    }
+    void someError(Throwable t) throws Exception {
+        instT = t;
+        instCnt++;
+    }
+    
+    @Test public void createOneTimeQueryInstance() {
+        OnLocationTest t = new OnLocationTest();
+        
+        net.java.html.geo.Position.Handle h = InstanceHandle.createQuery(t);
+        h.setHighAccuracy(false);
+        h.setTimeout(1000L);
+        h.setMaximumAge(1000L);
+        if (h.isSupported()) h.start();
+        h.stop();
+    }
+    
+    @Test public void onInstanceCallback() throws Throwable {
+        OnLocationTest t = new OnLocationTest();
+        net.java.html.geo.Position.Handle h = InstanceHandle.createWatch(t);
+        h.onLocation(new Position(0L, null));
+        assertEquals(t.instCnt, 1, "One callback made");
+    }
+
+    @Test public void onInstanceError() throws Throwable {
+        net.java.html.geo.Position.Handle h = InstanceHandle.createWatch(this);
+        InterruptedException e = new InterruptedException();
+        h.onError(e);
+        assertEquals(instCnt, 1, "One callback made");
+        assertEquals(instT, e, "The same exception passed in");
+    }
+
+    @Test public void createRepeatableWatch() {
+        OnLocationTest t = new OnLocationTest();
+        
+        net.java.html.geo.Position.Handle h = InstanceHandle.createWatch(t);
+        h.setHighAccuracy(false);
+        h.setTimeout(1000L);
+        h.setMaximumAge(1000L);
+        if (h.isSupported()) h.start();
+        h.stop();
+    }
+    
+    @OnLocation(onError = "errParam") void withParam(Position pos, int param) {
+        instCnt = param;
+    }
+    
+    void errParam(Exception ex, int param) {
+        instCnt = param;
+    }
+}


[18/24] incubator-netbeans-html4j git commit: [INFRA-15006] Initial donation of HTML/Java NetBeans API. Equivalent to the content of html4j-donation-review.zip donated as part of ApacheNetBeansDonation1.zip with SHA256 being 7f2ca0f61953a190613c9a0fbcc1b

Posted by jt...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/main/java/org/netbeans/html/boot/spi/Fn.java
----------------------------------------------------------------------
diff --git a/boot/src/main/java/org/netbeans/html/boot/spi/Fn.java b/boot/src/main/java/org/netbeans/html/boot/spi/Fn.java
new file mode 100644
index 0000000..c6deffb
--- /dev/null
+++ b/boot/src/main/java/org/netbeans/html/boot/spi/Fn.java
@@ -0,0 +1,388 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.spi;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import net.java.html.js.JavaScriptBody;
+import org.netbeans.html.boot.impl.FnContext;
+
+/** Represents single JavaScript function that can be invoked. 
+ * Created via {@link Presenter#defineFn(java.lang.String, java.lang.String...)}.
+ *
+ * @author Jaroslav Tulach
+ */
+public abstract class Fn {
+    private final Presenter presenter;
+    
+    /**
+     * @deprecated Ineffective as of 0.6. 
+     * Provide a presenter via {@link #Fn(org.netbeans.html.boot.spi.Fn.Presenter)}
+     * constructor
+     */
+    @Deprecated
+    protected Fn() {
+        this(null);
+    }
+    
+    /** Creates new function object and associates it with given presenter.
+     * 
+     * @param presenter the browser presenter associated with this function
+     * @since 0.6 
+     */
+    protected Fn(Presenter presenter) {
+        this.presenter = presenter;
+    }
+
+    /** True, if currently active presenter is the same as presenter this
+     * function has been created for via {@link #Fn(org.netbeans.html.boot.spi.Fn.Presenter)}.
+     * 
+     * @return true, if proper presenter is used
+     */
+    public final boolean isValid() {
+        return presenter != null && FnContext.currentPresenter(false) == presenter;
+    }
+    
+    /** Helper method to check if the provided instance is valid function.
+     * Checks if the parameter is non-null and if so, does {@link #isValid()}
+     * check.
+     * 
+     * @param fnOrNull function or <code>null</code>
+     * @return true if the parameter is non-null and valid
+     * @since 0.7
+     */
+    public static boolean isValid(Fn fnOrNull) {
+        return fnOrNull != null && fnOrNull.isValid();
+    }
+
+    /** Helper method to find current presenter and ask it to define new
+     * function by calling {@link Presenter#defineFn(java.lang.String, java.lang.String...)}.
+     * 
+     * @param caller the class who wishes to define the function
+     * @param code the body of the function (can reference <code>this</code> and <code>names</code> variables)
+     * @param names names of individual parameters
+     * @return the function object that can be {@link Fn#invoke(java.lang.Object, java.lang.Object...) invoked}
+     *    - can return <code>null</code> if there is {@link #activePresenter() no presenter}
+     * @since 0.7
+     */
+    public static Fn define(Class<?> caller, String code, String... names) {
+        return define(caller, false, code, names);
+    }
+
+    /** Helper method to find current presenter and ask it to define new
+     * function.
+     * 
+     * @param caller the class who wishes to define the function
+     * @param keepParametersAlive whether Java parameters should survive in JavaScript
+     *   after the method invocation is over
+     * @param code the body of the function (can reference <code>this</code> and <code>names</code> variables)
+     * @param names names of individual parameters
+     * @return the function object that can be {@link Fn#invoke(java.lang.Object, java.lang.Object...) invoked}
+     *    - can return <code>null</code> if there is {@link #activePresenter() no presenter}
+     * @since 1.1
+     */
+    public static Fn define(Class<?> caller, boolean keepParametersAlive, String code, String... names) {
+        final Presenter p = FnContext.currentPresenter(false);
+        if (p == null) {
+            return null;
+        }
+        if (p instanceof KeepAlive) {
+            boolean[] arr;
+            if (!keepParametersAlive && names.length > 0) {
+                arr = new boolean[names.length];
+                for (int i = 0; i < arr.length; i++) {
+                    arr[i] = false;
+                }
+            } else {
+                arr = null;
+            }
+            return ((KeepAlive)p).defineFn(code, names, arr);
+        }
+        return p.defineFn(code, names);
+    }
+    
+    private static final Map<String,Set<Presenter>> LOADED = new HashMap<String, Set<Presenter>>();
+    
+    /** Wraps function to ensure that the script represented by <code>resource</code>
+     * gets loaded into the browser environment before the function <code>fn</code>
+     * is executed.
+     * 
+     * @param fn original function to call (if <code>null</code> returns <code>null</code>)
+     * @param caller the class who wishes to define/call the function
+     * @param resource resources (accessible via {@link ClassLoader#getResource(java.lang.String)}) 
+     *   with a <em>JavaScript</em> that is supposed to loaded into the browser
+     *   environment
+     * @return function that ensures the script is loaded and then delegates
+     *   to <code>fn</code>. Returns <code>null</code> if the input <code>fn</code> is null
+     * @since 0.7
+     */
+    public static Fn preload(final Fn fn, final Class<?> caller, final String resource) {
+        if (fn == null) {
+            return null;
+        }
+        return new Fn(fn.presenter()) {
+            @Override
+            public Object invoke(Object thiz, Object... args) throws Exception {
+                loadResource();
+                return fn.invoke(thiz, args);
+            }
+
+            @Override
+            public void invokeLater(Object thiz, Object... args) throws Exception {
+                loadResource();
+                fn.invokeLater(thiz, args);
+            }
+            
+            private void loadResource() throws Exception {
+                Presenter p = presenter();
+                if (p == null) {
+                    p = FnContext.currentPresenter(false);
+                }
+                if (p != null) {
+                    Set<Presenter> there = LOADED.get(resource);
+                    if (there == null) {
+                        there = new HashSet<Presenter>();
+                        LOADED.put(resource, there);
+                    }
+                    if (there.add(p)) {
+                        final ClassLoader l = caller.getClassLoader();
+                        InputStream is = l.getResourceAsStream(resource);
+                        if (is == null && resource.startsWith("/")) {
+                            is = l.getResourceAsStream(resource.substring(1));
+                        }
+                        if (is == null) {
+                            throw new IOException("Cannot find " + resource + " in " + l);
+                        }
+                        try {
+                            InputStreamReader r = new InputStreamReader(is, "UTF-8");
+                            p.loadScript(r);
+                        } finally {
+                            is.close();
+                        }
+                    }
+                }
+            }
+        };
+    }
+
+    
+    /** The currently active presenter.
+     * 
+     * @return the currently active presenter or <code>null</code>
+     * @since 0.7
+     */
+    public static Presenter activePresenter() {
+        return FnContext.currentPresenter(false);
+    }
+    
+    /** Activates given presenter. Used to associate the native 
+     * JavaScript code specified by 
+     * {@link JavaScriptBody} annotation with certain presenter:
+     * <pre>
+     * try ({@link Closeable} c = Fn.activate(presenter)) {
+     *   doCallsInPresenterContext();
+     * }
+     * </pre>
+     * 
+     * @param p the presenter that should be active until closable is closed
+     * @return the closable to close
+     * @since 0.7
+     */
+    public static Closeable activate(Presenter p) {
+        return FnContext.activate(p);
+    }
+    
+    /** Invokes the defined function with specified <code>this</code> and
+     * appropriate arguments.
+     * 
+     * @param thiz the meaning of <code>this</code> inside of the JavaScript
+     *   function - can be <code>null</code>
+     * @param args arguments for the function
+     * @return return value from the function
+     * @throws Exception if something goes wrong, as exception may be thrown
+     */
+    public abstract Object invoke(Object thiz, Object... args) throws Exception;
+
+    /** Invokes the defined function with specified <code>this</code> and
+     * appropriate arguments asynchronously. The invocation may be 
+     * happen <em>"later"</em>.
+     * 
+     * @param thiz the meaning of <code>this</code> inside of the JavaScript
+     *   function - can be <code>null</code>
+     * @param args arguments for the function
+     * @throws Exception if something goes wrong, as exception may be thrown
+     * @since 0.7.6
+     */
+    public void invokeLater(Object thiz, Object... args) throws Exception {
+        invoke(thiz, args);
+    }
+    
+    /** Provides the function implementation access to the presenter provided
+     * in {@link #Fn(org.netbeans.html.boot.spi.Fn.Presenter) the constructor}.
+     * 
+     * @return presenter passed in the constructor (may be, but should not be <code>null</code>)
+     * @since 0.7
+     */
+    protected final Presenter presenter() {
+        return presenter;
+    }
+    
+    /** The representation of a <em>presenter</em> - usually a browser window.
+     * Should be provided by a library included in the application and registered
+     * in <code>META-INF/services</code>, for example with
+     * <code>@ServiceProvider(service = Fn.Presenter.class)</code> annotation.
+     * <p>
+     * Since 0.7 a presenter may implement {@link Executor} interface, in case
+     * it supports single threaded execution environment. The executor's
+     * {@link Executor#execute(java.lang.Runnable)} method is then supposed
+     * to invoke the runnable immediately (in case we are on the right thread
+     * already) or return and asynchronously invoke the runnable later on the
+     * right thread (if we are on wrong thread).
+     */
+    public interface Presenter {
+        /** Creates new function with given parameter names and provided body.
+         * 
+         * @param code the body of the function. Can refer to variables named
+         *   as <code>names</code>
+         * @param names names of parameters of the function - these will be 
+         *   available when the <code>code</code> body executes
+         * 
+         * @return function that can be later invoked
+         */
+        public Fn defineFn(String code, String... names);
+        
+        /** Opens the browser, loads provided page and when the
+         * page is ready, it calls back to the provider runnable.
+         * 
+         * @param page the URL for the page to display
+         * @param onPageLoad callback when the page is ready
+         */
+        public void displayPage(URL page, Runnable onPageLoad);
+        
+        /** Loads a script into the browser JavaScript interpreter and 
+         * executes it.
+         * @param code the script to execute
+         * @throws Exception if something goes wrong, throw an exception
+         */
+        public void loadScript(Reader code) throws Exception;
+    }
+    
+    /** Additional interface to be implemented by {@link Presenter}s that
+     * wish to control what objects are passed into the JavaScript virtual 
+     * machine.
+     * <p>
+     * If a JavaScript engine makes callback to Java method that returns 
+     * a value, the {@link #toJavaScript(java.lang.Object)} method is
+     * consulted to convert the Java value to something reasonable inside
+     * JavaScript VM.
+     * <p>
+     * <em>Note:</em> The implementation based on <em>JavaFX</em> <code>WebView</code>
+     * uses this interface to convert Java arrays to JavaScript ones.
+     * 
+     * @see Presenter
+     * @since 0.7
+     */
+    public interface ToJavaScript {
+        /** Convert a Java return value into some object suitable for
+         * JavaScript virtual machine.
+         * 
+         * @param toReturn the Java object to be returned
+         * @return the replacement value to return instead
+         */
+        public Object toJavaScript(Object toReturn);
+    }
+    
+    /** Additional interface to be implemented by {@link Presenter}s that
+     * need to convert JavaScript object (usually array) to Java object 
+     * when calling back from JavaScript to Java.
+     * <p>
+     * <em>Note:</em> The implementation based on <em>JavaFX</em>
+     * <code>WebView</code> uses this interface to convert JavaScript arrays to
+     * Java ones.
+      * 
+     * @since 0.7
+     */
+    public interface FromJavaScript {
+        /** Convert a JavaScript object into suitable Java representation
+         * before a Java method is called with this object as an argument.
+         * 
+         * @param js the JavaScript object
+         * @return replacement object for 
+         */
+        public Object toJava(Object js);
+    }
+
+    /** Additional interface to {@link Presenter} to control more precisely
+     * garbage collection behavior of individual parameters. See 
+     * {@link JavaScriptBody#keepAlive()} attribute for description of the
+     * actual behavior of the interface.
+     * 
+     * @since 1.1
+     */
+    public interface KeepAlive {
+        /** Creates new function with given parameter names and provided body.
+         * 
+         * @param code the body of the function. Can refer to variables named
+         *   as <code>names</code>
+         * @param names names of parameters of the function - these will be 
+         *   available when the <code>code</code> body executes
+         * @param keepAlive array of booleans describing for each parameter
+         *   whether it should be kept alive or not. Length of the array
+         *   must be the same as length of <code>names</code> array. The
+         *   array may be <code>null</code> to signal that all parameters
+         *   should be <em>kept alive</em>.
+         * 
+         * @return function that can be later invoked
+         */
+        public Fn defineFn(String code, String[] names, boolean[] keepAlive);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/main/java/org/netbeans/html/boot/spi/package.html
----------------------------------------------------------------------
diff --git a/boot/src/main/java/org/netbeans/html/boot/spi/package.html b/boot/src/main/java/org/netbeans/html/boot/spi/package.html
new file mode 100644
index 0000000..d4ef66e
--- /dev/null
+++ b/boot/src/main/java/org/netbeans/html/boot/spi/package.html
@@ -0,0 +1,54 @@
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<!DOCTYPE html>
+<html>
+    <body>
+        <div>Interfaces for integrators of various execution environments.</div>
+        Not really interesting for clients. The clients should rather use
+        {@link net.java.html.boot.BrowserBuilder} to launch their applications,
+        or (if they need to do some JavaScript calls themselves) look at
+        {@link net.java.html.js.JavaScriptBody} annotation and its usage.
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/main/resources/net/java/html/boot/html4j.txt
----------------------------------------------------------------------
diff --git a/boot/src/main/resources/net/java/html/boot/html4j.txt b/boot/src/main/resources/net/java/html/boot/html4j.txt
new file mode 100644
index 0000000..0552164
--- /dev/null
+++ b/boot/src/main/resources/net/java/html/boot/html4j.txt
@@ -0,0 +1,43 @@
+====
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+====
+

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/test/java/net/java/html/boot/BrowserBuilderTest.java
----------------------------------------------------------------------
diff --git a/boot/src/test/java/net/java/html/boot/BrowserBuilderTest.java b/boot/src/test/java/net/java/html/boot/BrowserBuilderTest.java
new file mode 100644
index 0000000..c4644fa
--- /dev/null
+++ b/boot/src/test/java/net/java/html/boot/BrowserBuilderTest.java
@@ -0,0 +1,134 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.boot;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.file.FileVisitResult;
+import java.nio.file.FileVisitor;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Locale;
+import static org.testng.Assert.*;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class BrowserBuilderTest {
+    private File dir;
+    private File index;
+    
+    public BrowserBuilderTest() {
+    }
+    
+    @BeforeMethod public void prepareFiles() throws IOException {
+        dir = File.createTempFile("test", ".dir");
+        dir.delete();
+        assertTrue(dir.mkdirs(), "Dir successfully created: " + dir);
+        
+        index = new File(dir, "index.html");
+        index.createNewFile();
+        
+        System.setProperty("browser.rootdir", dir.getPath());
+    }
+    
+    @AfterMethod public void clearFiles() throws IOException {
+        Files.walkFileTree(dir.toPath(), new FileVisitor<Path>() {
+            @Override
+            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
+                return FileVisitResult.CONTINUE;
+            }
+
+            @Override
+            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+                Files.delete(file);
+                return FileVisitResult.CONTINUE;
+            }
+
+            @Override
+            public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
+                return FileVisitResult.CONTINUE;
+            }
+
+            @Override
+            public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
+                Files.delete(dir);
+                return FileVisitResult.CONTINUE;
+            }
+        });
+    }
+    
+
+    @Test public void findsZhCN() throws IOException {
+        File zh = new File(dir, "index_zh.html"); zh.createNewFile();
+        File zhCN = new File(dir, "index_zh_CN.html"); zhCN.createNewFile();
+        
+        IOException[] mal = { null };
+        URL url = BrowserBuilder.findLocalizedResourceURL("index.html", Locale.SIMPLIFIED_CHINESE, mal, BrowserBuilder.class);
+        
+        assertEquals(url, zhCN.toURI().toURL(), "Found both suffixes");
+    }
+    
+    @Test public void findsZh() throws IOException {
+        File zh = new File(dir, "index_zh.html"); zh.createNewFile();
+        
+        IOException[] mal = { null };
+        URL url = BrowserBuilder.findLocalizedResourceURL("index.html", Locale.SIMPLIFIED_CHINESE, mal, BrowserBuilder.class);
+        
+        assertEquals(url, zh.toURI().toURL(), "Found one suffix");
+    }
+
+    @Test public void findsIndex() throws IOException {
+        IOException[] mal = { null };
+        URL url = BrowserBuilder.findLocalizedResourceURL("index.html", Locale.SIMPLIFIED_CHINESE, mal, BrowserBuilder.class);
+        
+        assertEquals(url, index.toURI().toURL(), "Found root file");
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/test/java/org/netbeans/html/boot/impl/Arithm.java
----------------------------------------------------------------------
diff --git a/boot/src/test/java/org/netbeans/html/boot/impl/Arithm.java b/boot/src/test/java/org/netbeans/html/boot/impl/Arithm.java
new file mode 100644
index 0000000..c4a3cd3
--- /dev/null
+++ b/boot/src/test/java/org/netbeans/html/boot/impl/Arithm.java
@@ -0,0 +1,53 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.impl;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class Arithm {
+    public int sumTwo(int a, int b) {
+        return a + b;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/test/java/org/netbeans/html/boot/impl/Compile.java
----------------------------------------------------------------------
diff --git a/boot/src/test/java/org/netbeans/html/boot/impl/Compile.java b/boot/src/test/java/org/netbeans/html/boot/impl/Compile.java
new file mode 100644
index 0000000..6862e6a
--- /dev/null
+++ b/boot/src/test/java/org/netbeans/html/boot/impl/Compile.java
@@ -0,0 +1,307 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.impl;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.tools.Diagnostic;
+import javax.tools.DiagnosticListener;
+import javax.tools.FileObject;
+import javax.tools.ForwardingJavaFileManager;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+import javax.tools.JavaFileObject.Kind;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.StandardLocation;
+import javax.tools.ToolProvider;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.fail;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+final class Compile implements DiagnosticListener<JavaFileObject> {
+    private final List<Diagnostic<? extends JavaFileObject>> errors = 
+            new ArrayList<Diagnostic<? extends JavaFileObject>>();
+    private final Map<String, byte[]> classes;
+    private final String pkg;
+    private final String cls;
+    private final String html;
+    private final String sourceLevel;
+
+    private Compile(String html, String code, String sl) throws IOException {
+        this.pkg = findPkg(code);
+        this.cls = findCls(code);
+        this.html = html;
+        this.sourceLevel = sl;
+        classes = compile(html, code);
+    }
+
+    /** Performs compilation of given HTML page and associated Java code
+     */
+    public static Compile create(String html, String code) throws IOException {
+        return create(html, code, "1.7");
+    }
+    static Compile create(String html, String code, String sourceLevel) throws IOException {
+        return new Compile(html, code, sourceLevel);
+    }
+    
+    /** Checks for given class among compiled resources */
+    public byte[] get(String res) {
+        return classes.get(res);
+    }
+    
+    /** Obtains errors created during compilation.
+     */
+    public List<Diagnostic<? extends JavaFileObject>> getErrors() {
+        return getDiagnostics(Diagnostic.Kind.ERROR);
+    }
+    public List<Diagnostic<? extends JavaFileObject>> getDiagnostics(Diagnostic.Kind kind) {
+        List<Diagnostic<? extends JavaFileObject>> err;
+        err = new ArrayList<Diagnostic<? extends JavaFileObject>>();
+        for (Diagnostic<? extends JavaFileObject> diagnostic : errors) {
+            if (diagnostic.getKind() == kind) {
+                err.add(diagnostic);
+            }
+        }
+        return err;
+    }
+    
+    private Map<String, byte[]> compile(final String html, final String code) throws IOException {
+        StandardJavaFileManager sjfm = ToolProvider.getSystemJavaCompiler().getStandardFileManager(this, null, null);
+
+        final Map<String, ByteArrayOutputStream> class2BAOS;
+        class2BAOS = new HashMap<String, ByteArrayOutputStream>();
+
+        JavaFileObject file = new SimpleJavaFileObject(URI.create("mem://mem"), Kind.SOURCE) {
+            @Override
+            public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+                return code;
+            }
+        };
+        final JavaFileObject htmlFile = new SimpleJavaFileObject(URI.create("mem://mem2"), Kind.OTHER) {
+            @Override
+            public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+                return html;
+            }
+
+            @Override
+            public InputStream openInputStream() throws IOException {
+                return new ByteArrayInputStream(html.getBytes());
+            }
+        };
+        
+        final URI scratch;
+        try {
+            scratch = new URI("mem://mem3");
+        } catch (URISyntaxException ex) {
+            throw new IOException(ex);
+        }
+        
+        JavaFileManager jfm = new ForwardingJavaFileManager<JavaFileManager>(sjfm) {
+            @Override
+            public FileObject getFileForOutput(JavaFileManager.Location location, String packageName, String relativeName, FileObject sibling) throws IOException {
+                try {
+                    return new VirtFO(new URI("mem://resource/" + relativeName), Kind.OTHER, relativeName);
+                } catch (URISyntaxException ex) {
+                    throw new IllegalStateException(ex);
+                }
+            }
+            
+            
+            @Override
+            public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) throws IOException {
+                if (kind  == Kind.CLASS) {
+                    final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+
+                    class2BAOS.put(className.replace('.', '/') + ".class", buffer);
+                    return new SimpleJavaFileObject(sibling.toUri(), kind) {
+                        @Override
+                        public OutputStream openOutputStream() throws IOException {
+                            return buffer;
+                        }
+                    };
+                }
+                
+                if (kind == Kind.SOURCE) {
+                    final String n = className.replace('.', '/') + ".java";
+                    final URI un;
+                    try {
+                        un = new URI("mem://" + n);
+                    } catch (URISyntaxException ex) {
+                        throw new IOException(ex);
+                    }
+                    return new VirtFO(un/*sibling.toUri()*/, kind, n);
+                }
+                
+                throw new IllegalStateException();
+            }
+
+            @Override
+            public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException {
+                if (location == StandardLocation.SOURCE_PATH) {
+                    if (packageName.equals(pkg)) {
+                        return htmlFile;
+                    }
+                    if (packageName.isEmpty() && relativeName.startsWith(pkg.replace('.', '/'))) {
+                        return htmlFile;
+                    }
+                }
+                
+                return null;
+            }
+
+            @Override
+            public boolean isSameFile(FileObject a, FileObject b) {
+                if (a instanceof VirtFO && b instanceof VirtFO) {
+                    return ((VirtFO)a).getName().equals(((VirtFO)b).getName());
+                }
+                
+                return super.isSameFile(a, b);
+            }
+
+            class VirtFO extends SimpleJavaFileObject {
+
+                private final String n;
+
+                public VirtFO(URI uri, Kind kind, String n) {
+                    super(uri, kind);
+                    this.n = n;
+                }
+                private final ByteArrayOutputStream data = new ByteArrayOutputStream();
+
+                @Override
+                public OutputStream openOutputStream() throws IOException {
+                    return data;
+                }
+
+                @Override
+                public String getName() {
+                    return n;
+                }
+
+                @Override
+                public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+                    data.close();
+                    return new String(data.toByteArray());
+                }
+            }
+        };
+
+        ToolProvider.getSystemJavaCompiler().getTask(null, jfm, this, /*XXX:*/Arrays.asList("-source", sourceLevel, "-target", "1.7"), null, Arrays.asList(file)).call();
+
+        Map<String, byte[]> result = new HashMap<String, byte[]>();
+
+        for (Map.Entry<String, ByteArrayOutputStream> e : class2BAOS.entrySet()) {
+            result.put(e.getKey(), e.getValue().toByteArray());
+        }
+
+        return result;
+    }
+
+
+    @Override
+    public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
+        errors.add(diagnostic);
+    }
+    private static String findPkg(String java) throws IOException {
+        Pattern p = Pattern.compile("package\\p{javaWhitespace}*([\\p{Alnum}\\.]+)\\p{javaWhitespace}*;", Pattern.MULTILINE);
+        Matcher m = p.matcher(java);
+        if (!m.find()) {
+            throw new IOException("Can't find package declaration in the java file");
+        }
+        String pkg = m.group(1);
+        return pkg;
+    }
+    private static String findCls(String java) throws IOException {
+        Pattern p = Pattern.compile("class\\p{javaWhitespace}*([\\p{Alnum}\\.]+)\\p{javaWhitespace}", Pattern.MULTILINE);
+        Matcher m = p.matcher(java);
+        if (!m.find()) {
+            throw new IOException("Can't find package declaration in the java file");
+        }
+        String cls = m.group(1);
+        return cls;
+    }
+
+    String getHtml() {
+        String fqn = "'" + pkg + '.' + cls + "'";
+        return html.replace("'${fqn}'", fqn);
+    }
+    void assertErrors() {
+        assertFalse(getErrors().isEmpty(), "There are supposed to be some errors");
+    }
+
+    void assertError(String expMsg) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("Can't find ").append(expMsg).append(" among:");
+        for (Diagnostic<? extends JavaFileObject> e : errors) {
+            String msg = e.getMessage(Locale.US);
+            if (msg.contains(expMsg)) {
+                return;
+            }
+            sb.append("\n");
+            sb.append(msg);
+        }
+        fail(sb.toString());
+    }
+
+    void assertNoErrors() {
+        assertTrue(getErrors().isEmpty(), "No errors expected: " + getErrors());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/test/java/org/netbeans/html/boot/impl/CountFnCreationTest.java
----------------------------------------------------------------------
diff --git a/boot/src/test/java/org/netbeans/html/boot/impl/CountFnCreationTest.java b/boot/src/test/java/org/netbeans/html/boot/impl/CountFnCreationTest.java
new file mode 100644
index 0000000..8e3f0e2
--- /dev/null
+++ b/boot/src/test/java/org/netbeans/html/boot/impl/CountFnCreationTest.java
@@ -0,0 +1,123 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.impl;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.Reader;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Enumeration;
+import net.java.html.js.JavaScriptBody;
+import net.java.html.js.JavaScriptResource;
+import org.netbeans.html.boot.spi.Fn;
+import static org.testng.Assert.assertEquals;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+@JavaScriptResource("empty.js")
+public class CountFnCreationTest implements Fn.Presenter {
+    private int cnt;
+    
+    @JavaScriptBody(args = {}, body = "return;")
+    public static native void body();
+    
+    @Test public void countManyTimes() throws Exception {
+        class Res implements FindResources {
+            @Override
+            public void findResources(String path, Collection<? super URL> results, boolean oneIsEnough) {
+                try {
+                    ClassLoader l = CountFnCreationTest.class.getClassLoader();
+                    Enumeration<URL> en = l.getResources(path);
+                    while (en.hasMoreElements()) {
+                        results.add(en.nextElement());
+                    }
+                } catch (IOException ex) {
+                    throw new IllegalStateException(ex);
+                }
+            }
+        }
+        ClassLoader l = FnUtils.newLoader(new Res(), this, CountFnCreationTest.class.getClassLoader().getParent());
+        Method m = l.loadClass(CountFnCreationTest.class.getName()).getMethod("body");
+        Closeable c = Fn.activate(this);
+        try {
+            assertEquals(cnt, 0, "No functions yet");
+            m.invoke(null);
+            assertEquals(cnt, 1, "One function defined");
+            m.invoke(null);
+            assertEquals(cnt, 1, "Still one function");
+        } finally {
+            c.close();
+        }
+    }
+
+    @Override
+    public Fn defineFn(String code, String... names) {
+        cnt++;
+        return new MyFn(this);
+    }
+
+    @Override
+    public void displayPage(URL page, Runnable onPageLoad) {
+    }
+
+    @Override
+    public void loadScript(Reader code) throws Exception {
+    }
+    
+    private static final class MyFn extends Fn {
+
+        public MyFn(Presenter presenter) {
+            super(presenter);
+        }
+
+        @Override
+        public java.lang.Object invoke(java.lang.Object thiz, java.lang.Object... args) throws Exception {
+            return null;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/test/java/org/netbeans/html/boot/impl/FnTest.java
----------------------------------------------------------------------
diff --git a/boot/src/test/java/org/netbeans/html/boot/impl/FnTest.java b/boot/src/test/java/org/netbeans/html/boot/impl/FnTest.java
new file mode 100644
index 0000000..d7c55ab
--- /dev/null
+++ b/boot/src/test/java/org/netbeans/html/boot/impl/FnTest.java
@@ -0,0 +1,186 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.impl;
+
+import java.io.Closeable;
+import java.io.Flushable;
+import java.io.IOException;
+import java.io.Reader;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import javax.script.Invocable;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+import org.netbeans.html.boot.spi.Fn;
+import static org.testng.Assert.assertEquals;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class FnTest extends JsClassLoaderBase {
+    private static Fn.Presenter presenter;
+    
+    public FnTest() {
+    }
+
+    @BeforeClass
+    public static void createClassLoader() throws Exception {
+        ScriptEngineManager sem = new ScriptEngineManager();
+        final ScriptEngine eng = sem.getEngineByMimeType("text/javascript");
+        
+        final URL my = FnTest.class.getProtectionDomain().getCodeSource().getLocation();
+        ClassLoader parent = JsClassLoaderTest.class.getClassLoader().getParent();
+        final URLClassLoader ul = new URLClassLoader(new URL[] { my }, parent);
+        
+        class Impl implements FindResources, Fn.Presenter {
+            @Override
+            public void findResources(String path, Collection<? super URL> results, boolean oneIsEnough) {
+                URL u = ul.findResource(path);
+                if (u != null) {
+                    results.add(u);
+                }
+            }
+
+            @Override
+            public Fn defineFn(String code, String... names) {
+                StringBuilder sb = new StringBuilder();
+                sb.append("(function() {");
+                sb.append("return function(");
+                String sep = "";
+                for (String n : names) {
+                    sb.append(sep);
+                    sb.append(n);
+                    sep = ", ";
+                }
+                sb.append(") {");
+                sb.append(code);
+                sb.append("};");
+                sb.append("})()");
+                try {
+                    final java.lang.Object val = eng.eval(sb.toString());
+                    return new Fn(this) {
+                        @Override
+                        public java.lang.Object invoke(java.lang.Object thiz, java.lang.Object... args) throws Exception {
+                            List<java.lang.Object> all = new ArrayList<java.lang.Object>(args.length + 1);
+                            all.add(thiz == null ? val : thiz);
+                            all.addAll(Arrays.asList(args));
+                            Invocable inv = (Invocable)eng;
+                            try {
+                                java.lang.Object ret = inv.invokeMethod(val, "call", all.toArray());
+                                return val.equals(ret) ? null : ret;
+                            } catch (ScriptException ex) {
+                                throw ex;
+                            }
+                        }
+                    };
+                } catch (ScriptException ex) {
+                    throw new LinkageError("Can't parse: " + sb, ex);
+                }
+            }
+
+            @Override
+            public void displayPage(URL resource, Runnable r) {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public void loadScript(Reader code) throws Exception {
+                eng.eval(code);
+            }
+        }
+        Impl impl = new Impl();
+        ClassLoader loader = FnUtils.newLoader(impl, impl, parent);
+        presenter = impl;
+        
+        Closeable close = FnContext.activate(impl);
+        methodClass = loader.loadClass(JsMethods.class.getName());
+        close.close();
+    }
+    
+    @Test public void flushingPresenter() throws IOException {
+        class FP implements Fn.Presenter, Flushable {
+            int flush;
+
+            @Override
+            public Fn defineFn(String code, String... names) {
+                throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+            }
+
+            @Override
+            public void displayPage(URL page, Runnable onPageLoad) {
+                throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+            }
+
+            @Override
+            public void loadScript(Reader code) throws Exception {
+                throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+            }
+
+            @Override
+            public void flush() throws IOException {
+                flush++;
+            }
+        }
+        
+        FP p = new FP();
+        Closeable c1 = Fn.activate(p);
+        Closeable c2 = Fn.activate(p);
+        c2.close();
+        assertEquals(p.flush, 0, "No flush yet");
+        c1.close();
+        assertEquals(p.flush, 1, "Now flushed");
+    }
+
+    @BeforeMethod public void initPresenter() {
+        FnContext.currentPresenter(presenter);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/test/java/org/netbeans/html/boot/impl/JavaScriptProcesorTest.java
----------------------------------------------------------------------
diff --git a/boot/src/test/java/org/netbeans/html/boot/impl/JavaScriptProcesorTest.java b/boot/src/test/java/org/netbeans/html/boot/impl/JavaScriptProcesorTest.java
new file mode 100644
index 0000000..1ef07b5
--- /dev/null
+++ b/boot/src/test/java/org/netbeans/html/boot/impl/JavaScriptProcesorTest.java
@@ -0,0 +1,167 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.impl;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Locale;
+import javax.tools.Diagnostic;
+import javax.tools.JavaFileObject;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class JavaScriptProcesorTest {
+    
+    @Test public void detectCallbackToNonExistingClass() throws IOException {
+        String code = "package x.y.z;\n"
+            + "import net.java.html.js.JavaScriptBody;\n"
+            + "class X {\n"
+            + "  @JavaScriptBody(args={\"r\"}, javacall=true, body =\n"
+            + "    \"r.@java.lang.Runable::run()();\"\n" // typo
+            + "  )\n"
+            + "  private static native void callback(Runnable r);\n"
+            + "}\n";
+        
+        Compile c = Compile.create("", code);
+        c.assertErrors();
+        c.assertError("java.lang.Runable"); // typo
+    }
+
+    @Test public void detectCallbackToNonExistingMethod() throws IOException {
+        String code = "package x.y.z;\n"
+            + "import net.java.html.js.JavaScriptBody;\n"
+            + "class X {\n"
+            + "  @JavaScriptBody(args={\"r\"}, javacall=true, body =\n"
+            + "    \"r.@java.lang.Runnable::cancel()();\"\n"
+            + "  )\n"
+            + "  private static native void callback(Runnable r);\n"
+            + "}\n";
+        
+        Compile c = Compile.create("", code);
+        c.assertErrors();
+        c.assertError("method cancel");
+    }
+
+    @Test public void detectCallbackToNonExistingParams() throws IOException {
+        String code = "package x.y.z;\n"
+            + "import net.java.html.js.JavaScriptBody;\n"
+            + "class X {\n"
+            + "  @JavaScriptBody(args={\"r\"}, javacall=true, body =\n"
+            + "    \"r.@java.lang.Runnable::run(I)(10);\"\n"
+            + "  )\n"
+            + "  private static native void callback(Runnable r);\n"
+            + "}\n";
+        
+        Compile c = Compile.create("", code);
+        c.assertErrors();
+        c.assertError("wrong parameters: (I)");
+    }
+
+    @Test public void objectTypeParamsAreOK() throws IOException {
+        String code = "package x.y.z;\n"
+            + "import net.java.html.js.JavaScriptBody;\n"
+            + "class X {\n"
+            + "  @JavaScriptBody(args={\"r\"}, javacall=true, body =\n"
+            + "    \"r.@java.lang.Object::equals(Ljava/lang/Object;)(null);\"\n"
+            + "  )\n"
+            + "  private static native void testEqual(Object r);\n"
+            + "}\n";
+        
+        Compile c = Compile.create("", code);
+        c.assertNoErrors();
+    }
+    
+    @Test public void misorderNotified() throws IOException {
+        String code = "package x.y.z;\n"
+            + "import net.java.html.js.JavaScriptBody;\n"
+            + "class X {\n"
+            + "  @JavaScriptBody(args={\"r\", \"a\", \"b\"}, body =\"\"\n"
+            + "  )\n"
+            + "  private static native void testEqual(Object p, String q, int r);\n"
+            + "}\n";
+        
+        Compile c = Compile.create("", code);
+        List<Diagnostic<? extends JavaFileObject>> warnings = c.getDiagnostics(Diagnostic.Kind.WARNING);
+        assertTrue(warnings.size() >= 1, "There are warnings: " + warnings);
+        for (Diagnostic<? extends JavaFileObject> w : warnings) {
+            if (w.getMessage(Locale.US).contains("Actual method parameter names and args")) {
+                return;
+            }
+        }
+        fail("Expecting order warning: " + warnings);
+    }
+
+    @Test public void needJavaScriptBodyToUseResource() throws IOException {
+        String code = "package x.y.z;\n"
+            + "import net.java.html.js.JavaScriptResource;\n"
+            + "@JavaScriptResource(\"x.html\")\n"
+            + "class X {\n"
+            + "  private static native void callback(Runnable r);\n"
+            + "}\n";
+        
+        Compile c = Compile.create("", code);
+        c.assertErrors();
+        c.assertError("needs @JavaScriptBody");
+    }
+    
+    @Test public void generatesCallbacksThatReturnObject() throws Exception {
+        Class<?> callbacksForTestPkg = Class.forName("org.netbeans.html.boot.impl.$JsCallbacks$");
+        Method m = callbacksForTestPkg.getDeclaredMethod("java_lang_Runnable$run$", Runnable.class);
+        assertEquals(m.getReturnType(), java.lang.Object.class, "All methods always return object");
+    }
+    
+    @Test public void hasInstanceField() throws Exception {
+        Class<?> callbacksForTestPkg = Class.forName("org.netbeans.html.boot.impl.$JsCallbacks$");
+        Field f = callbacksForTestPkg.getDeclaredField("VM");
+        f.setAccessible(true);
+        assertTrue(callbacksForTestPkg.isInstance(f.get(null)), "Singleton field VM");
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/test/java/org/netbeans/html/boot/impl/JsCallbackTest.java
----------------------------------------------------------------------
diff --git a/boot/src/test/java/org/netbeans/html/boot/impl/JsCallbackTest.java b/boot/src/test/java/org/netbeans/html/boot/impl/JsCallbackTest.java
new file mode 100644
index 0000000..5b68349
--- /dev/null
+++ b/boot/src/test/java/org/netbeans/html/boot/impl/JsCallbackTest.java
@@ -0,0 +1,86 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.impl;
+
+import static org.testng.Assert.*;
+import org.testng.annotations.Test;
+
+/** Verify behavior of the callback parser.
+ *
+ * @author Jaroslav Tulach
+ */
+public class JsCallbackTest {
+    
+    public JsCallbackTest() {
+    }
+    @Test public void missingTypeSpecification() {
+        String body = "console[attr] = function(msg) {\n"
+        + "  @org.netbeans.html.charts.Main::log(msg);\n"
+        + "};\n";
+        JsCallback instance = new JsCallbackImpl();
+        try {
+            String result = instance.parse(body);
+            fail("The parsing should fail!");
+        } catch (IllegalStateException ex) {
+            // OK
+        }
+    }
+
+
+    public class JsCallbackImpl extends JsCallback {
+        private String ident;
+        private String fqn;
+        private String method;
+        private String params;
+        
+        @Override
+        public CharSequence callMethod(String ident, String fqn, String method, String params) {
+            this.ident = ident;
+            this.fqn = fqn;
+            this.method = method;
+            this.params = params;
+            return "call";
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/test/java/org/netbeans/html/boot/impl/JsClassLoaderBase.java
----------------------------------------------------------------------
diff --git a/boot/src/test/java/org/netbeans/html/boot/impl/JsClassLoaderBase.java b/boot/src/test/java/org/netbeans/html/boot/impl/JsClassLoaderBase.java
new file mode 100644
index 0000000..9b458d2
--- /dev/null
+++ b/boot/src/test/java/org/netbeans/html/boot/impl/JsClassLoaderBase.java
@@ -0,0 +1,285 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.impl;
+
+import java.io.Closeable;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.HashMap;
+import java.util.Map;
+import org.netbeans.html.boot.spi.Fn;
+import org.testng.Assert;
+import static org.testng.Assert.*;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class JsClassLoaderBase {
+    protected static Class<?> methodClass;
+    
+    public JsClassLoaderBase() {
+    }
+    
+    @BeforeMethod
+    public void assertClassDefined() {
+        assertNotNull(methodClass, "BeforeClass set up code should provide methodClass");
+    }
+
+    @Test public void noParamMethod() throws Throwable {
+        Method plus = methodClass.getMethod("fortyTwo");
+        try {
+            final java.lang.Object val = plus.invoke(null);
+            assertTrue(val instanceof Number, "A number returned " + val);
+            assertEquals(((Number)val).intValue(), 42);
+        } catch (InvocationTargetException ex) {
+            throw ex.getTargetException();
+        }
+    }
+    
+    @Test public void testExecuteScript() throws Throwable {
+        Method plus = methodClass.getMethod("plus", int.class, int.class);
+        try {
+            assertEquals(plus.invoke(null, 10, 20), 30);
+        } catch (InvocationTargetException ex) {
+            throw ex.getTargetException();
+        }
+    }
+
+    @Test public void overloadedMethod() throws Throwable {
+        Method plus = methodClass.getMethod("plus", int.class);
+        try {
+            assertEquals(plus.invoke(null, 10), 10);
+        } catch (InvocationTargetException ex) {
+            throw ex.getTargetException();
+        }
+    }
+    
+    @Test public void instanceMethod() throws Throwable {
+        Method plus = methodClass.getMethod("plusInst", int.class);
+        java.lang.Object inst = methodClass.newInstance();
+        try {
+            assertEquals(plus.invoke(inst, 10), 10);
+        } catch (InvocationTargetException ex) {
+            throw ex.getTargetException();
+        }
+    }
+    
+    @Test public void staticThis() throws Throwable {
+        Method st = methodClass.getMethod("staticThis");
+        try {
+            assertNull(st.invoke(null));
+        } catch (InvocationTargetException ex) {
+            throw ex.getTargetException();
+        }
+    }
+
+    @Test public void getThis() throws Throwable {
+        java.lang.Object th = methodClass.newInstance();
+        Method st = methodClass.getMethod("getThis");
+        try {
+            assertEquals(st.invoke(th), th);
+        } catch (InvocationTargetException ex) {
+            throw ex.getTargetException();
+        }
+    }
+
+    @Test public void primitiveArrayReturn() throws Throwable {
+        Method st = methodClass.getMethod("both", double.class, double.class);
+        Throwable ex;
+        try {
+            java.lang.Object arr = st.invoke(null, 2, 5);
+            ex = null;
+        } catch (InvocationTargetException invoke) {
+            ex = invoke.getTargetException();
+        }
+        assertTrue(ex instanceof ClassCastException, "Primitive arrays aren't returned from JavaScript: " + ex);
+    }
+    
+    @Test public void truth() throws Throwable {
+        Method st = methodClass.getMethod("truth");
+        assertTrue((st.getModifiers() & Modifier.STATIC) != 0, "Is static");
+        assertEquals(st.invoke(null), Boolean.TRUE, "Can return boolean");
+    }
+    
+    @Test public void callback() throws Throwable {
+        class R implements Runnable {
+            int cnt;
+            
+            @Override
+            public void run() {
+                cnt++;
+            }
+        }
+        R r = new R();
+        
+        Method inc = methodClass.getMethod("callback", Runnable.class);
+        inc.invoke(null, r);
+        
+        assertEquals(r.cnt, 1, "Callback happened");
+    }
+    
+    @Test public void sumArray() throws Throwable {
+        Method st = methodClass.getMethod("sumArr", int[].class);
+        assertEquals(st.invoke(null, new int[] { 1, 2, 3 }), 6, "1+2+3 is six");
+    }
+    
+    @Test public void javaScriptResource() throws Throwable {
+        try {
+            Method st = methodClass.getMethod("useExternalMul", int.class, int.class);
+            assertEquals(st.invoke(null, 6, 7), 42, "Meaning of JavaScript?");
+        } catch (InvocationTargetException ex) {
+            throw ex.getTargetException();
+        }
+    }
+    
+    @Test public void callJavaScriptMethodOnOwnClass() throws Throwable {
+        try {
+            java.lang.Object thiz = methodClass.newInstance();
+            Method st = methodClass.getMethod("returnYourSelf", methodClass);
+            assertEquals(st.invoke(null, thiz), thiz, "Returns this");
+        } catch (InvocationTargetException ex) {
+            throw ex.getTargetException();
+        }
+    }
+    
+    @Test public void callStaticJavaMethod() throws Throwable {
+        Method st = methodClass.getMethod("staticCallback", int.class, int.class);
+        assertEquals(st.invoke(null, 6, 7), 42, "Meaning of JavaScript?");
+    }
+
+    @Test public void callStaticStringParamMethod() throws Throwable {
+        Method st = methodClass.getMethod("parseInt", String.class);
+        assertEquals(st.invoke(null, "42"), 42, "Meaning of JavaScript?");
+    }
+
+    @Test public void passEnum() throws Throwable {
+        Class<?> enmClazz = methodClass.getDeclaredClasses()[0];
+        assertTrue(Enum.class.isAssignableFrom(enmClazz), "It is an enum: " + enmClazz);
+        Class<? extends Enum> enmClazz2 = enmClazz.asSubclass(Enum.class);
+        Method st = methodClass.getMethod("fromEnum", enmClazz);
+        
+        java.lang.Object valueB = Enum.valueOf(enmClazz2, "B");
+        assertEquals(st.invoke(null, valueB), "B", "Converts to string");
+    }
+    
+    @Test public void firstLong() throws Throwable {
+        Method st = methodClass.getMethod("chooseLong", boolean.class, boolean.class, long.class, long.class);
+        assertEquals(st.invoke(null, true, false, 10, 20), 10L, "Take first value");
+    }
+
+    @Test public void secondLong() throws Throwable {
+        Method st = methodClass.getMethod("chooseLong", boolean.class, boolean.class, long.class, long.class);
+        assertEquals(st.invoke(null, false, true, 10, 20), 20L, "Take 2nd value");
+    }
+
+    @Test public void bothLong() throws Throwable {
+        Method st = methodClass.getMethod("chooseLong", boolean.class, boolean.class, long.class, long.class);
+        assertEquals(st.invoke(null, true, true, 10, 20), 30L, "Take both values");
+    }
+    
+    @Test public void recordError() throws Throwable {
+        Method st = methodClass.getMethod("recordError", java.lang.Object.class);
+        assertEquals(st.invoke(methodClass.newInstance(), "Hello"), "Hello", "The same parameter returned");
+    }
+    
+    @Test public void plusOrMul() throws Throwable {
+        Method st = methodClass.getMethod("plusOrMul", int.class, int.class);
+        assertNotNull(Fn.activePresenter(), "Is there a presenter?");
+        Closeable c = Fn.activate(null);
+        try {
+            assertNull(Fn.activePresenter(), "No presenter now");
+            assertEquals(st.invoke(null, 6, 7), 42, "Mul in Java");
+        } finally {
+            c.close();
+        }
+        assertNotNull(Fn.activePresenter(), "Is there a presenter again");
+        assertEquals(st.invoke(null, 6, 7), 13, "Plus in JavaScript");
+        c = Fn.activate(null);
+        try {
+            assertNull(Fn.activePresenter(), "No presenter again");
+            assertEquals(st.invoke(null, 6, 7), 42, "Mul in Java");
+        } finally {
+            c.close();
+        }
+        assertNotNull(Fn.activePresenter(), "Is there a presenter again");
+        assertEquals(st.invoke(null, 6, 7), 13, "Plus in JavaScript again");
+    }
+    
+    @Test public void arrayInOut() throws Throwable {
+        String[] arr = { "Ahoj" };
+        Method st = methodClass.getMethod("arr", java.lang.Object[].class);
+        java.lang.Object ret = st.invoke(null, (java.lang.Object) arr);
+        assertTrue(ret instanceof java.lang.Object[], "Expecting array: " + ret);
+        java.lang.Object[] res = (java.lang.Object[]) ret;
+        assertEquals(res.length, 1, "One element");
+        assertEquals(res[0], "Ahoj", "The right string");
+    }
+
+    @Test public void parametricCallback() throws Throwable {
+        Map<String,Number> map = new HashMap<String, Number>();
+        Method st = methodClass.getMethod("callParamTypes", Map.class, int.class);
+        st.invoke(null, map, 42);
+        assertEquals(map.get("key").intValue(), 42, "The right value");
+    }
+    
+   @Test public void checkTheTypeOfThrownException() throws Throwable {
+        FnContext.currentPresenter(null);
+        assertNull(Fn.activePresenter(), "No presenter is activer right now");
+        java.lang.Object res = null;
+        try {
+            Method st = methodClass.getMethod("plus", int.class, int.class);
+            try {
+                res = st.invoke(null, 40, 2);
+                Assert.fail("Native method should throw IllegalStateException. Was: " + res);
+            } catch (InvocationTargetException ex) {
+                throw ex.getTargetException();
+            }
+        } catch (IllegalStateException ex) {
+            assertEquals(ex.getMessage(), "No presenter active. Use BrwsrCtx.execute!");
+        }
+    }    
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/test/java/org/netbeans/html/boot/impl/JsClassLoaderTest.java
----------------------------------------------------------------------
diff --git a/boot/src/test/java/org/netbeans/html/boot/impl/JsClassLoaderTest.java b/boot/src/test/java/org/netbeans/html/boot/impl/JsClassLoaderTest.java
new file mode 100644
index 0000000..d69a761
--- /dev/null
+++ b/boot/src/test/java/org/netbeans/html/boot/impl/JsClassLoaderTest.java
@@ -0,0 +1,161 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.impl;
+
+import java.io.Closeable;
+import java.io.Reader;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import javax.script.Invocable;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+import org.netbeans.html.boot.spi.Fn;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class JsClassLoaderTest extends JsClassLoaderBase{
+    private static Fn.Presenter loader;
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        ScriptEngineManager sem = new ScriptEngineManager();
+        final ScriptEngine eng = sem.getEngineByMimeType("text/javascript");
+        
+        final URL my = JsClassLoaderTest.class.getProtectionDomain().getCodeSource().getLocation();
+        ClassLoader parent = JsClassLoaderTest.class.getClassLoader().getParent();
+        final URLClassLoader ul = new URLClassLoader(new URL[] { my }, parent);
+        class MyCL extends FnUtils.JsClassLoaderImpl implements Fn.Presenter {
+
+            public MyCL(ClassLoader parent) {
+                super(parent, null, null);
+            }
+            
+            @Override
+            protected URL findResource(String name) {
+                return ul.getResource(name);
+            }
+            @Override
+            public Fn defineFn(String code, String... names) {
+                StringBuilder sb = new StringBuilder();
+                sb.append("(function() {");
+                sb.append("return function(");
+                String sep = "";
+                for (String n : names) {
+                    sb.append(sep);
+                    sb.append(n);
+                    sep = ", ";
+                }
+                sb.append(") {");
+                sb.append(code);
+                sb.append("};");
+                sb.append("})()");
+                try {
+                    final java.lang.Object val = eng.eval(sb.toString());
+                    return new Fn(this) {
+                        @Override
+                        public java.lang.Object invoke(java.lang.Object thiz, java.lang.Object... args) throws Exception {
+                            List<java.lang.Object> all = new ArrayList<java.lang.Object>(args.length + 1);
+                            all.add(thiz == null ? val : thiz);
+                            all.addAll(Arrays.asList(args));
+                            Invocable inv = (Invocable)eng;
+                            try {
+                                java.lang.Object ret = inv.invokeMethod(val, "call", all.toArray());
+                                return val.equals(ret) ? null : ret;
+                            } catch (Exception ex) {
+                                throw ex;
+                            }
+                        }
+                    };
+                } catch (ScriptException ex) {
+                    throw new LinkageError("Can't parse: " + sb, ex);
+                }
+            }
+
+            @Override
+            protected Enumeration<URL> findResources(String name) {
+                URL u = findResource(name);
+                List<URL> arr = new ArrayList<URL>();
+                if (u != null) {
+                    arr.add(u);
+                }
+                return Collections.enumeration(arr);
+            }
+
+            @Override
+            public void loadScript(Reader code) throws ScriptException {
+                eng.eval(code);
+            }
+
+            @Override
+            public void displayPage(URL page, Runnable onPageLoad) {
+                throw new UnsupportedOperationException();
+            }
+        };
+        
+        MyCL l = new MyCL(parent);
+        Closeable close = FnContext.activate(l);
+        methodClass = l.loadClass(JsMethods.class.getName());
+        close.close();
+        loader = l;
+    }
+    
+    @BeforeMethod public void initPresenter() {
+        FnContext.currentPresenter(loader);
+    }
+
+    @AfterClass
+    public static void cleanUp() {
+        methodClass = null;
+    }
+}
\ No newline at end of file



[20/24] incubator-netbeans-html4j git commit: [INFRA-15006] Initial donation of HTML/Java NetBeans API. Equivalent to the content of html4j-donation-review.zip donated as part of ApacheNetBeansDonation1.zip with SHA256 being 7f2ca0f61953a190613c9a0fbcc1b

Posted by jt...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-truffle/src/test/java/net/java/html/boot/truffle/TruffleJavaScriptTest.java
----------------------------------------------------------------------
diff --git a/boot-truffle/src/test/java/net/java/html/boot/truffle/TruffleJavaScriptTest.java b/boot-truffle/src/test/java/net/java/html/boot/truffle/TruffleJavaScriptTest.java
new file mode 100644
index 0000000..ac4c4f5
--- /dev/null
+++ b/boot-truffle/src/test/java/net/java/html/boot/truffle/TruffleJavaScriptTest.java
@@ -0,0 +1,164 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.boot.truffle;
+
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.vm.PolyglotEngine;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executors;
+import net.java.html.boot.BrowserBuilder;
+import org.netbeans.html.boot.spi.Fn;
+import org.netbeans.html.json.tck.JavaScriptTCK;
+import org.netbeans.html.json.tck.KOTest;
+import org.testng.Assert;
+import org.testng.SkipException;
+import org.testng.annotations.Factory;
+import org.testng.annotations.Test;
+import static org.testng.Assert.assertEquals;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class TruffleJavaScriptTest {
+    private static Class<?> browserClass;
+    private static Fn.Presenter browserPresenter;
+    
+    public TruffleJavaScriptTest() {
+    }
+
+    @Factory public static Object[] compatibilityTests() throws Exception {
+        PolyglotEngine engine = PolyglotEngine.newBuilder().build();
+        PolyglotEngine.Value result = null;
+        try {
+            result = engine.eval(Source.fromText("6 * 7", "test.js").withMimeType("text/javascript"));
+        } catch (Exception notSupported) {
+            if (notSupported.getMessage().contains("text/javascript")) {
+                return new Object[] { new Skip(true, notSupported.getMessage()) };
+            }
+        }
+        assertEquals(42, result.as(Number.class).intValue(), "Executed OK");
+
+        final BrowserBuilder bb = BrowserBuilder.newBrowser(TrufflePresenters.create(SingleCase.JS)).
+            loadClass(TruffleJavaScriptTest.class).
+            loadPage("empty.html").
+            invoke("initialized");
+
+        Executors.newSingleThreadExecutor().submit(new Runnable() {
+            @Override
+            public void run() {
+                bb.showAndWait();
+            }
+        });
+
+        List<Object> res = new ArrayList<>();
+        Class<? extends Annotation> test = 
+            loadClass().getClassLoader().loadClass(KOTest.class.getName()).
+            asSubclass(Annotation.class);
+
+        Class[] arr = (Class[]) loadClass().getDeclaredMethod("tests").invoke(null);
+        for (Class c : arr) {
+            if (c.getSimpleName().contains("GC")) {
+                continue;
+            }
+            for (Method m : c.getMethods()) {
+                if (m.getAnnotation(test) != null) {
+                    res.add(new SingleCase(browserPresenter, m));
+                }
+            }
+        }
+        return res.toArray();
+    }
+
+    static synchronized Class<?> loadClass() throws InterruptedException {
+        while (browserClass == null) {
+            TruffleJavaScriptTest.class.wait();
+        }
+        return browserClass;
+    }
+    
+    public static synchronized void ready(Class<?> browserCls) throws Exception {
+        browserClass = browserCls;
+        browserPresenter = Fn.activePresenter();
+        TruffleJavaScriptTest.class.notifyAll();
+    }
+    
+    public static void initialized() throws Exception {
+        Assert.assertSame(TruffleJavaScriptTest.class.getClassLoader(),
+            ClassLoader.getSystemClassLoader(),
+            "No special classloaders"
+        );
+        TruffleJavaScriptTest.ready(Tck.class);
+    }
+
+    public static final class Tck extends JavaScriptTCK {
+
+        public static Class[] tests() {
+            return testClasses();
+        }
+    }
+
+    public static final class Skip {
+        private final String message;
+        private final boolean fail;
+
+        public Skip(String message) {
+            this(false, message);
+        }
+
+        Skip(boolean fail, String message) {
+            this.message = message;
+            this.fail = fail;
+        }
+
+        @Test
+        public void needsGraalVMToExecuteTheTests() {
+            if (fail) {
+                throw new SkipException(message);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/pom.xml
----------------------------------------------------------------------
diff --git a/boot/pom.xml b/boot/pom.xml
new file mode 100644
index 0000000..e1ec2ff
--- /dev/null
+++ b/boot/pom.xml
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2016 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<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">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.netbeans.html</groupId>
+    <artifactId>pom</artifactId>
+    <version>2.0-SNAPSHOT</version>
+  </parent>
+  <groupId>org.netbeans.html</groupId>
+  <artifactId>net.java.html.boot</artifactId>
+  <version>2.0-SNAPSHOT</version>
+  <packaging>bundle</packaging>
+  <name>Browser Bootstrap</name>
+  <url>http://maven.apache.org</url>
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <publicPackages>net.java.html.js,net.java.html.boot,org.netbeans.html.boot.spi</publicPackages>
+  </properties>
+  <build>
+      <plugins>
+          <plugin>
+              <groupId>org.apache.felix</groupId>
+              <artifactId>maven-bundle-plugin</artifactId>
+              <configuration>
+                  <instructions>
+                      <Agent-Class>org.netbeans.html.boot.impl.JsAgent</Agent-Class>
+                      <Premain-Class>org.netbeans.html.boot.impl.JsAgent</Premain-Class>
+                      <Eclipse-BuddyPolicy>dependent</Eclipse-BuddyPolicy>
+                      <Require-Capability>osgi.extender;resolution:=optional;filter:="(osgi.extender=osgi.serviceloader.processor)",osgi.serviceloader;filter:="(osgi.serviceloader=org.netbeans.html.boot.spi.Fn$Presenter)";cardinality:=multiple;resolution:=optional</Require-Capability>
+                  </instructions>
+              </configuration>
+          </plugin>
+      </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>org.ow2.asm</groupId>
+      <artifactId>asm</artifactId>
+      <type>jar</type>
+      <scope>provided</scope>
+      <optional>true</optional>
+    </dependency>
+    <dependency>
+      <groupId>org.testng</groupId>
+      <artifactId>testng</artifactId>
+      <scope>test</scope>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+      <groupId>org.netbeans.api</groupId>
+      <artifactId>org-openide-util-lookup</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.netbeans.html</groupId>
+      <artifactId>net.java.html</artifactId>
+      <version>${project.version}</version>
+      <type>jar</type>
+    </dependency>
+  </dependencies>
+    <description>Builder to launch your Java/HTML based application.</description>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/main/java/net/java/html/boot/BrowserBuilder.java
----------------------------------------------------------------------
diff --git a/boot/src/main/java/net/java/html/boot/BrowserBuilder.java b/boot/src/main/java/net/java/html/boot/BrowserBuilder.java
new file mode 100644
index 0000000..3bb8d52
--- /dev/null
+++ b/boot/src/main/java/net/java/html/boot/BrowserBuilder.java
@@ -0,0 +1,519 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.boot;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import java.net.HttpURLConnection;
+import java.net.JarURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.ProtectionDomain;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.ServiceLoader;
+import java.util.concurrent.Executor;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import net.java.html.BrwsrCtx;
+import net.java.html.js.JavaScriptBody;
+import org.netbeans.html.boot.spi.Fn;
+import org.netbeans.html.boot.spi.Fn.Presenter;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.context.spi.Contexts.Id;
+import org.netbeans.html.boot.impl.FindResources;
+import org.netbeans.html.boot.impl.FnContext;
+
+/** Use this builder to launch your Java/HTML based application. Typical
+ * usage in a main method of your application looks like this: 
+ * <pre>
+ * 
+ * <b>public static void</b> <em>main</em>(String... args) {
+ *     BrowserBuilder.{@link #newBrowser newBrowser()}.
+ *          {@link #loadClass(java.lang.Class) loadClass(YourMain.class)}.
+ *          {@link #loadPage(java.lang.String) loadPage("index.html")}.
+ *          {@link #locale(java.util.Locale) locale}({@link Locale#getDefault()}).
+ *          {@link #invoke(java.lang.String, java.lang.String[]) invoke("initialized", args)}.
+ *          {@link #showAndWait()};
+ *     System.exit(0);
+ * }
+ * </pre>
+ * The above will load <code>YourMain</code> class via
+ * a special classloader, it will locate an <code>index.html</code> (relative
+ * to <code>YourMain</code> class) and show it in a browser window. When the
+ * initialization is over, a <b>public static</b> method <em>initialized</em>
+ * in <code>YourMain</code> will be called with provided string parameters.
+ * <p>
+ * This module provides only API for building browsers. To use it properly one
+ * also needs an implementation on the classpath of one's application. For example
+ * use: <pre>
+ * &lt;dependency&gt;
+ *   &lt;groupId&gt;org.netbeans.html&lt;/groupId&gt;
+ *   &lt;artifactId&gt;net.java.html.boot.fx&lt;/artifactId&gt;
+ *   &lt;scope&gt;runtime&lt;/scope&gt;
+ * &lt;/dependency&gt;
+ * </pre>
+ *
+ * @author Jaroslav Tulach
+ */
+public final class BrowserBuilder {
+    private static final Logger LOG = Logger.getLogger(BrowserBuilder.class.getName());
+    
+    private String resource;
+    private Class<?> clazz;
+    private Runnable onLoad;
+    private String methodName;
+    private String[] methodArgs;
+    private final Object[] context;
+    private ClassLoader loader;
+    private Locale locale;
+    
+    private BrowserBuilder(Object[] context) {
+        this.context = context;
+    }
+
+    /** Entry method to obtain a new browser builder. Follow by calling 
+     * its instance methods like {@link #loadClass(java.lang.Class)} and
+     * {@link #loadPage(java.lang.String)}.
+     * Since introduction of {@link Id technology identifiers} the 
+     * provided <code>context</code> objects are also passed to the 
+     * {@link BrwsrCtx context} when it is being 
+     * {@link Contexts#newBuilder(java.lang.Object...) created}
+     * and can influence the selection
+     * of available technologies 
+     * (like {@link org.netbeans.html.json.spi.Technology},
+     * {@link org.netbeans.html.json.spi.Transfer} or
+     * {@link org.netbeans.html.json.spi.WSTransfer}) by name.
+     * 
+     * @param context any instances that should be available to the builder -
+     *   implementation dependant
+     * @return new browser builder
+     */
+    public static BrowserBuilder newBrowser(Object... context) {
+        return new BrowserBuilder(context);
+    }
+    
+    /** The class to load when the browser is initialized. This class
+     * is loaded by a special classloader (that supports {@link JavaScriptBody}
+     * and co.). 
+     * 
+     * @param mainClass the class to load and resolve when the browser is ready
+     * @return this builder
+     */
+    public BrowserBuilder loadClass(Class<?> mainClass) {
+        this.clazz = mainClass;
+        return this;
+    }
+    
+    /** Allows one to specify a runnable that should be invoked when a load
+     * of a page is finished. This method may be used in addition or instead
+     * of {@link #loadClass(java.lang.Class)} and 
+     * {@link #invoke(java.lang.String, java.lang.String...)} methods.
+     * 
+     * @param r the code to run when the page is loaded
+     * @return this builder
+     * @since 0.8.1
+     */
+    public BrowserBuilder loadFinished(Runnable r) {
+        this.onLoad = r;
+        return this;
+    }
+
+    /** Page to load into the browser. If the <code>page</code> represents
+     * a {@link URL} known to the Java system, the URL is passed to the browser. 
+     * If system property <code>browser.rootdir</code> is specified, then a
+     * file <code>page</code> relative to this directory is used as the URL.
+     * If no such file exists, the system seeks for the 
+     * resource via {@link Class#getResource(java.lang.String)}
+     * method (relative to the {@link #loadClass(java.lang.Class) specified class}). 
+     * If such resource is not found, a file relative to the location JAR
+     * that contains the {@link #loadClass(java.lang.Class) main class} is 
+     * searched for.
+     * <p>
+     * The search honors provided {@link #locale}, if specified.
+     * E.g. it will prefer <code>index_cs.html</code> over <code>index.html</code>
+     * if the locale is set to <code>cs_CZ</code>.
+     * 
+     * @param page the location (relative, absolute, or URL) of a page to load
+     * @return this builder
+     */
+    public BrowserBuilder loadPage(String page) {
+        this.resource = page;
+        return this;
+    }
+    
+    /** Locale to use when searching for an initial {@link #loadPage(java.lang.String) page to load}.
+     * Localization is best done by providing different versions of the 
+     * initial page with appropriate suffixes (like <code>index_cs.html</code>).
+     * Then one can call this method with value of {@link Locale#getDefault()}
+     * to instruct the builder to use the user's current locale.
+     * 
+     * @param locale the locale to use or <code>null</code> if no suffix search should be performed
+     * @return this builder
+     * @since 1.0
+     */
+    public BrowserBuilder locale(Locale locale) {
+        this.locale = locale;
+        return this;
+    }
+    
+    /** Specifies callback method to notify the application that the browser is ready.
+     * There should be a <b>public static</b> method in the class specified
+     * by {@link #loadClass(java.lang.Class)} which takes an array of {@link String}
+     * argument. The method is called on the browser dispatch thread one
+     * the browser finishes loading of the {@link #loadPage(java.lang.String) HTML page}.
+     * 
+     * @param methodName name of a method to seek for
+     * @param args parameters to pass to the method
+     * @return this builder
+     */
+    public BrowserBuilder invoke(String methodName, String... args) {
+        this.methodName = methodName;
+        this.methodArgs = args;
+        return this;
+    }
+
+    /** Loader to use when searching for classes to initialize. 
+     * If specified, this loader is going to be used to load {@link Presenter}
+     * and {@link Contexts#fillInByProviders(java.lang.Class, org.netbeans.html.context.spi.Contexts.Builder) fill} {@link BrwsrCtx} in.
+     * Specifying special classloader may be useful in modular systems, 
+     * like OSGi, where one needs to load classes from many otherwise independent
+     * modules.
+     * 
+     * @param l the loader to use (or <code>null</code>)
+     * @return this builder
+     * @since 0.9
+     */
+    public BrowserBuilder classloader(ClassLoader l) {
+        this.loader = l;
+        return this;
+    }
+
+    /** Shows the browser, loads specified page in and executes the 
+     * {@link #invoke(java.lang.String, java.lang.String[]) initialization method}.
+     * The method returns when the browser is closed.
+     * 
+     * @throws NullPointerException if some of essential parameters (like {@link #loadPage(java.lang.String) page} or
+     *    {@link #loadClass(java.lang.Class) class} have not been specified
+     */
+    public void showAndWait() {
+        if (resource == null) {
+            throw new NullPointerException("Need to specify resource via loadPage method");
+        }
+        
+        final Class<?> myCls;
+        if (clazz != null) {
+            myCls = clazz;
+        } else if (onLoad != null) {
+            myCls = onLoad.getClass();
+        } else {
+            throw new NullPointerException("loadClass, neither loadFinished was called!");
+        }
+        IOException mal[] = { null };
+        URL url = findLocalizedResourceURL(resource, locale, mal, myCls);
+        
+        Fn.Presenter dfnr = null;
+        for (Object o : context) {
+            if (o instanceof Fn.Presenter) {
+                dfnr = (Fn.Presenter)o;
+                break;
+            }
+        }
+
+        if (dfnr == null && loader != null) for (Fn.Presenter o : ServiceLoader.load(Fn.Presenter.class, loader)) {
+            dfnr = o;
+            break;
+        }
+        
+        if (dfnr == null) for (Fn.Presenter o : ServiceLoader.load(Fn.Presenter.class)) {
+            dfnr = o;
+            break;
+        }
+        
+        if (dfnr == null) {
+            throw new IllegalStateException("Can't find any Fn.Presenter");
+        }
+        
+        final ClassLoader activeLoader;
+        if (loader != null) {
+            final URL res = FnContext.isJavaScriptCapable(loader);
+            if (res != null) {
+                throw new IllegalStateException("Loader " + loader + 
+                    " cannot resolve @JavaScriptBody, because of " + res
+                );
+            }
+            activeLoader = loader;
+        } else {
+            final URL res = FnContext.isJavaScriptCapable(myCls.getClassLoader());
+            if (res == null) {
+                activeLoader = myCls.getClassLoader();
+            } else {
+                FImpl impl = new FImpl(myCls.getClassLoader());
+                activeLoader = FnContext.newLoader(res, impl, dfnr, myCls.getClassLoader().getParent());
+                if (activeLoader == null) {
+                    throw new IllegalStateException("Cannot find asm-5.0.jar classes!");
+                }
+            }
+        }
+        
+        final Fn.Presenter dP = dfnr;
+
+        class OnPageLoad implements Runnable {
+            @Override
+            public void run() {
+                try {
+                    final Fn.Presenter aP = Fn.activePresenter();
+                    final Fn.Presenter currentP = aP != null ? aP : dP;
+                    
+                    Thread.currentThread().setContextClassLoader(activeLoader);
+                    final Class<?> newClazz = onLoad != null ?
+                        myCls :
+                        Class.forName(myCls.getName(), true, activeLoader);
+                    Contexts.Builder cb = Contexts.newBuilder(context);
+                    if (!Contexts.fillInByProviders(newClazz, cb)) {
+                        LOG.log(Level.WARNING, "Using empty technology for {0}", newClazz);
+                    }
+                    if (currentP instanceof Executor) {
+                        cb.register(Executor.class, (Executor)currentP, 1000);
+                    }
+                    cb.register(Fn.Presenter.class, currentP, 1000);
+                    BrwsrCtx c = cb.build();
+
+                    class CallInitMethod implements Runnable {
+                        @Override
+                        public void run() {
+                            Throwable firstError = null;
+                            if (onLoad != null) {
+                                try {
+                                    FnContext.currentPresenter(currentP);
+                                    onLoad.run();
+                                } catch (Throwable ex) {
+                                    firstError = ex;
+                                } finally {
+                                    FnContext.currentPresenter(null);
+                                }
+                            }
+                            INIT: if (methodName != null) {
+                                if (methodArgs.length == 0) {
+                                    try {
+                                        Method m = newClazz.getMethod(methodName);
+                                        FnContext.currentPresenter(currentP);
+                                        m.invoke(null);
+                                        firstError = null;
+                                        break INIT;
+                                    } catch (Throwable ex) {
+                                        firstError = ex;
+                                    } finally {
+                                        FnContext.currentPresenter(null);
+                                    }
+                                }
+                                try {
+                                    Method m = newClazz.getMethod(methodName, String[].class);
+                                    FnContext.currentPresenter(currentP);
+                                    m.invoke(m, (Object) methodArgs);
+                                    firstError = null;
+                                } catch (Throwable ex) {
+                                    LOG.log(Level.SEVERE, "Can't call " + methodName + " with args " + Arrays.toString(methodArgs), ex);
+                                } finally {
+                                    FnContext.currentPresenter(null);
+                                }
+                            }
+                            if (firstError != null) {
+                                LOG.log(Level.SEVERE, "Can't initialize the view", firstError);
+                            }
+                        }
+                    }
+                    
+                    c.execute(new CallInitMethod());
+                } catch (ClassNotFoundException ex) {
+                    LOG.log(Level.SEVERE, "Can't load " + myCls.getName(), ex);
+                }
+            }
+        }
+        dfnr.displayPage(url, new OnPageLoad());
+    }
+
+    private static URL findResourceURL(String resource, String suffix, IOException[] mal, Class<?> relativeTo) {
+        if (suffix != null) {
+            int lastDot = resource.lastIndexOf('.');
+            if (lastDot != -1) {
+                resource = resource.substring(0, lastDot) + suffix + resource.substring(lastDot);
+            } else {
+                resource = resource + suffix;
+            }
+        }
+        
+        URL url = null;
+        try {
+            String baseURL = System.getProperty("browser.rootdir"); // NOI18N
+            if (baseURL != null) {
+                URL u = new File(baseURL, resource).toURI().toURL();
+                if (isReal(u)) {
+                    url = u;
+                }
+            } 
+            
+            {
+                URL u = new URL(resource);
+                if (suffix == null || isReal(u)) {
+                    url = u;
+                }
+                return url;
+            }
+        } catch (MalformedURLException ex) {
+            mal[0] = ex;
+        }
+        
+        if (url == null) {
+            url = relativeTo.getResource(resource);
+        }
+        if (url == null) {
+            final ProtectionDomain pd = relativeTo.getProtectionDomain();
+            if (pd != null && pd.getCodeSource() != null) {
+                URL jar = pd.getCodeSource().getLocation();
+                try {
+                    URL u = new URL(jar, resource);
+                    if (isReal(u)) {
+                        url = u;
+                    }
+                } catch (MalformedURLException ex) {
+                    ex.initCause(mal[0]);
+                    mal[0] = ex;
+                }
+            }
+        }
+        if (url == null) {
+            URL res = BrowserBuilder.class.getResource("html4j.txt");
+            LOG.log(Level.FINE, "Found html4j {0}", res);
+            if (res != null) {
+                try {
+                    URLConnection c = res.openConnection();
+                    LOG.log(Level.FINE, "testing : {0}", c);
+                    if (c instanceof JarURLConnection) {
+                        JarURLConnection jc = (JarURLConnection) c;
+                        URL base = jc.getJarFileURL();
+                        for (int i = 0; i < 50; i++) {
+                            URL u = new URL(base, resource);
+                            if (isReal(u)) {
+                                url = u;
+                                break;
+                            }
+                            base = new URL(base, "..");
+                        }
+                    }
+                } catch (IOException ex) {
+                    mal[0] = ex;
+                }
+            }
+        }
+        return url;
+    }
+
+    static URL findLocalizedResourceURL(String resource, Locale l, IOException[] mal, Class<?> relativeTo) {
+        URL url = null;
+        if (l != null) {
+            url = findResourceURL(resource, "_" + l.getLanguage() + "_" + l.getCountry(), mal, relativeTo);
+            if (url != null) {
+                return url;
+            }
+            url = findResourceURL(resource, "_" + l.getLanguage(), mal, relativeTo);
+        }
+        if (url != null) {
+            return url;
+        }
+        return findResourceURL(resource, null, mal, relativeTo);
+    }
+    
+    private static boolean isReal(URL u) {
+        try {
+            URLConnection conn = u.openConnection();
+            if (conn instanceof HttpURLConnection) {
+                HttpURLConnection hc = (HttpURLConnection) conn;
+                hc.setReadTimeout(5000);
+                if (hc.getResponseCode() >= 300) {
+                    throw new IOException("Wrong code: " + hc.getResponseCode());
+                }
+            }
+            InputStream is = conn.getInputStream();
+            is.close();
+            LOG.log(Level.FINE, "found real url: {0}", u);
+            return true;
+        } catch (IOException ignore) {
+            LOG.log(Level.FINE, "Cannot open " + u, ignore);
+            return false;
+        }
+    }
+
+    private static final class FImpl implements FindResources {
+        final ClassLoader l;
+
+        public FImpl(ClassLoader l) {
+            this.l = l;
+        }
+
+        @Override
+        public void findResources(String path, Collection<? super URL> results, boolean oneIsEnough) {
+            if (oneIsEnough) {
+                URL u = l.getResource(path);
+                if (u != null) {
+                    results.add(u);
+                }
+            } else {
+                try {
+                    Enumeration<URL> en = l.getResources(path);
+                    while (en.hasMoreElements()) {
+                        results.add(en.nextElement());
+                    }
+                } catch (IOException ex) {
+                    // no results
+                }
+            }
+        }
+        
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/main/java/net/java/html/boot/package.html
----------------------------------------------------------------------
diff --git a/boot/src/main/java/net/java/html/boot/package.html b/boot/src/main/java/net/java/html/boot/package.html
new file mode 100644
index 0000000..aea8c9b
--- /dev/null
+++ b/boot/src/main/java/net/java/html/boot/package.html
@@ -0,0 +1,54 @@
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<!DOCTYPE html>
+<html>
+    <body>
+        <div>{@link net.java.html.boot.BrowserBuilder Builder} class to bootstrap your Java/HTML based application.</div>
+        See {@link net.java.html.boot.BrowserBuilder} for description how to
+        launch your application. Look at {@link net.java.html.js.JavaScriptBody}
+        and its usage in case you want to directly talk between Java and
+        JavaScript.
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/main/java/net/java/html/js/JavaScriptBody.java
----------------------------------------------------------------------
diff --git a/boot/src/main/java/net/java/html/js/JavaScriptBody.java b/boot/src/main/java/net/java/html/js/JavaScriptBody.java
new file mode 100644
index 0000000..01f4977
--- /dev/null
+++ b/boot/src/main/java/net/java/html/js/JavaScriptBody.java
@@ -0,0 +1,152 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.js;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** Put this annotation on a method to provide its special implementation
+ * in JavaScript. This is a way to define <em>native</em> methods that 
+ * interact with the surrounding JavaScript environment. Check the list
+ * <a href="package-summary.html">use-cases</a> to see real world
+ * use of this annotation.
+ * <p>
+ * Visit an <a target="_blank" href="http://dew.apidesign.org/dew/#7102188">on-line demo</a>
+ * to play with {@link JavaScriptBody} annotation for real.
+ *
+ * @author Jaroslav Tulach
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target({ ElementType.METHOD, ElementType.CONSTRUCTOR })
+public @interface JavaScriptBody {
+    /** Names of parameters for the method generated method that can
+     * be referenced from {@link #body()}.
+     * 
+     * @return array of the names of parameters for the method
+     *    in JavaScript
+     */
+    public String[] args();
+    
+    /** The actual body of the method in JavaScript. This string will be
+     * put into generated header (last character is '{') and footer (e.g. '}').
+     * The body can reference provided arguments. In case of non-static
+     * instance method it may reference <code>this</code>. 
+     * 
+     * @return JavaScript body of a function which can access {@link #args()} and possibly
+     * <code>this</code>
+     */
+    public String body();
+
+    /** Should a special syntax for calling back into Java object be turned on?
+     * The syntax begins with <b>{@code @}</b> followed by fully qualified
+     * package name of the class. Now followed by <b>::</b> and a method in
+     * the class followed by its parameters enclosed inside <b>(...)</b>.
+     * This is the syntax one can use to call <code>run()</code> 
+     * method of {@link Runnable}:
+     * <pre>r.@java.lang.Runnable::run()()</pre>.
+     * One can also call static methods. Just use:
+     * <pre>var ten = @java.lang.Integer::parseInt(Ljava/lang/String;)("10")</pre>
+     * 
+     * @return true, if the script should be scanned for special callback
+     *   syntax
+     */
+    public boolean javacall() default false;
+
+    /** Should we wait before the JavaScript snippet execution finishes?
+     * Or not. 
+     * <p>
+     * Some implementations that recognize the {@link JavaScriptBody} annotation
+     * need to reschedule the JavaScript execution into different thread and
+     * then it is easier for them to perform the execution asynchronously
+     * and not wait for the result of the execution. This may however be
+     * unexpected (for example when one awaits a callback into Java)
+     * and as such it has to be explicitly allowed by specifying
+     * <code>wait4js = false</code>. Such methods need to return <code>void</code>.
+     * <p>
+     * Implementations that execute the JavaScript synchronously may ignore
+     * this attribute.
+     * <p>
+     * Implementations that delay execution of JavaScript need to guarantee
+     * the order of snippets. Those that were submitted sooner, need to be
+     * executed sooner. Each snippet need to be executed in a timely manner
+     * (e.g. by a second, or so) even if there are no other calls made
+     * in the main program.
+     * <p>
+     * 
+     * @since 0.7.6
+     * @return <code>false</code> in case one allows asynchronous execution
+     *   of the JavaScript snippet
+     */
+    public boolean wait4js() default true;
+    
+    /** Controls garbage collection behavior of method parameters.
+     * In general JavaScript garbage
+     * collection system makes it close to impossible to find out whether
+     * an object is supposed to be still used or not. Some systems have
+     * an external hooks to find that out (like <em>JavaFX</em> <code>WebView</code>),
+     * in some systems this information is not important (like the 
+     * <a href="http://bck2brwsr.apidesign.org">Bck2Brwsr</a> VM running
+     * all in JavaScript), but other execution systems just can't find that
+     * out. To prevent memory leaks on such systems and help them manage
+     * memory more effectively, those who define JavaScript interfacing 
+     * methods may indicate whether the non-primitive parameters passed
+     * in should be hold only for the time of method invocation or 
+     * for the whole application lifetime.
+     * <p>
+     * The default value is <code>true</code> as that is compatible with
+     * previous behavior and also prevents unwanted surprises when something
+     * garbage collects pre-maturaly. Framework developers are however 
+     * encouraged to use <code>keepAlive=false</code> as much as possible.
+     * 
+     * @return whether Java objects passed as parameters of the method
+     *   should be made guaranteed to be available JavaScript
+     *   even after the method invocation is over (e.g. prevent them to be
+     *   garbage collected in Java until it is known they are not needed
+     *   from JavaScript at all).
+     * 
+     * @since 1.1
+     */
+    public boolean keepAlive() default true;
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/main/java/net/java/html/js/JavaScriptResource.java
----------------------------------------------------------------------
diff --git a/boot/src/main/java/net/java/html/js/JavaScriptResource.java b/boot/src/main/java/net/java/html/js/JavaScriptResource.java
new file mode 100644
index 0000000..c14d10e
--- /dev/null
+++ b/boot/src/main/java/net/java/html/js/JavaScriptResource.java
@@ -0,0 +1,70 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.js;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** Include JavaScript libraries into your application easily.
+ * Annotate a Java/JavaScript bridging class with this annotation and
+ * once one of the class {@code @}{@link JavaScriptBody} annotated methods
+ * is called, it is guaranteed the JavaScript interpreter pre-load
+ * the content of here is specified resource. All other 
+ * {@code @}{@link JavaScriptBody} methods can then access objects created
+ * by precessing this {@link JavaScriptResource#value() java script resource}.
+ * The list of
+ * <a href="package-summary.html#library">use-cases</a> includes sample use
+ * of this annotation.
+ *
+ * @author Jaroslav Tulach
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.TYPE)
+public @interface JavaScriptResource {
+    /** The JavaScript file to load in before associated class can execute.
+     * @return relative path with respect to the annotated class
+     */
+    public String value();
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/main/java/net/java/html/js/package.html
----------------------------------------------------------------------
diff --git a/boot/src/main/java/net/java/html/js/package.html b/boot/src/main/java/net/java/html/js/package.html
new file mode 100644
index 0000000..f8160c0
--- /dev/null
+++ b/boot/src/main/java/net/java/html/js/package.html
@@ -0,0 +1,483 @@
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<!DOCTYPE html>
+<html>
+    <body>
+        <div>Essential support for those who write <em>native</em> methods communicating directly with JavaScript.</div>
+        Mix your Java and JavaScript code seamlessly - perform calls from Java
+        to JavaScript and back with as much freedom as JavaScript gives you
+        and as much type safety you can get from Java. Execute your code
+        in a headless testing environment or in a 
+        <a href="http://wiki.apidesign.org/wiki/FXBrwsr">JavaFX WebView</a>. 
+        When done, deploy to <a href="http://bck2brwsr.apidesign.org">real browsers</a>.
+        
+        <h3>Simple Meaning of World</h3>
+        The whole support is build around @<a href="JavaScriptBody.html">JavaScriptBody</a>
+        annotation. Use it to create parametrised JavaScript snippet easily
+        accessible from Java:
+<pre>
+{@code @}{@link net.java.html.js.JavaScriptBody}(args = {"x", "y"}, body = "return x + y;")
+<b>private static native int</b> meaning(<b>int</b> x, <b>int</b> y);
+</pre>
+        The above defines method <em>meaning</em> which sums two JavaScript
+        objects together (being invoked inside of a JavaScript interpreter).
+        The <em>meaning</em> method now becomes a properly typed Java
+        surface to your JavaScript code which can be directly
+        called from the rest of your Java code:
+<pre>        
+<b>public static void</b> main(String... args) {
+  <b>assert</b> 42 == meaning(40, 2) : <em>"Meaning of World should be 42!"</em>;
+}
+</pre>
+        <em>Real code tip:</em> real classes using this technique are
+        available online:
+        <a target="top" href="http://hg.netbeans.org/html4j/file/release-0.7/boot/src/test/java/org/netbeans/html/boot/impl/JsMethods.java">JsMethods</a> and
+        <a target="top" href="http://hg.netbeans.org/html4j/file/release-0.7/json-tck/src/main/java/net/java/html/js/tests/Bodies.java">Bodies</a>.
+        <p></p>
+        <em>Editing hint:</em> one can see the list of arguments of the
+        <em>meaning</em> is now duplicated - it is once specified in Java, 
+        and once inside of the {@link net.java.html.js.JavaScriptBody} 
+        array of <code>args</code>. This is necessary to keep the names of 
+        arguments accessible during runtime. However don't despair - there
+        is a code completion for the value of <code>args</code> attribute!
+        Just type the Java signature first and then press Ctrl+Space and the
+        right parameter names will be inserted for you.
+        
+        <a name="#library"><h3>Including JavaScript Libraries</h3></a>
+
+        Large amount of JavaScript code is easier to be delivered in whole
+        files rather than {@link net.java.html.js.JavaScriptBody small code snippets} -
+        that is also possible thanks to {@link net.java.html.js.JavaScriptResource}
+        annotation. Imagine file <code>mul.js</code> with following content:
+<pre> 
+<b>function</b> <em>mul</em>(x, y) { <b>return</b> x * y; }
+</pre>
+        Place the file next to your class and reference it with 
+        {@link net.java.html.js.JavaScriptResource the annotation}:
+<pre>
+{@code @}{@link net.java.html.js.JavaScriptResource}("mul.js") <b>class</b> Mul {
+
+  {@code @}{@link net.java.html.js.JavaScriptBody}(args = { "x", "y" }, body = "return <b>mul</b>(x, y);")
+  <b>public static native int</b> multiply(int x, int y);
+
+  <b>public static void</b> main(String... args) {
+    <b>assert</b> 42 == multiply(6, 7) : <em>"Meaning of World should be 42!"</em>;
+  }
+}
+</pre>
+        All the Java methods annotated {@link net.java.html.js.JavaScriptBody}
+        can now reference everything that is in the <code>mul.js</code> file -
+        e.g. the body of the <code>multiply</code> method can reference the
+        function <code>mul</code> and use it.
+        <p></p>
+        <em>Real code tip:</em>
+        <a target="top" href="http://hg.netbeans.org/html4j/file/release-0.7/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java">this</a> 
+        is the way 
+        the <a target="top" href="http://knockoutjs.com">knockout.js</a> library
+        is included in its <em>ko4j</em> library.
+        
+        <h3>Callback to Java</h3>
+        
+        Often JavaScript code needs to call back into the Java classes.
+        For example when a button in a browser is pressed and your code would
+        like to invoke a runnable to handle such situation:
+<pre> 
+{@code @}{@link net.java.html.js.JavaScriptBody}(args = {"id", "r"}, {@link net.java.html.js.JavaScriptBody#javacall() javacall} = true, body = "\n" + 
+"       document.getElementById(id).onclick = function() {\n" + 
+"        r.<em>{@code @}java.lang.Runnable::run()</em>();\n" + 
+"       };\n" + 
+"    ")
+<b>public static native void</b> onClick(String id, Runnable r);
+</pre>
+        As can be seen, there is a special syntax (starting with <b>@</b>) to 
+        properly identify the right Java method to call on a Java object passed
+        into the JavaScript interpreter. The syntax starts with a fully
+        qualified name of the class, followed by <b>::</b> and name of the 
+        method including signature of its parameters. In case of runnable,
+        this is just <em>()</em> as the method has no parameters, but the
+        signature can be more complicated. For example in case of following method
+<pre><b>static int</b> compare(<b>int</b> i1, String s1, <b>int</b> i2, String s2)
+</pre>        
+        it would be <em>(ILjava/lang/String;ILjava/lang/String;)</em> (btw. the
+        return type is not included in the signature). The actual parameters 
+        then follows. The JavaScript call to such compare method would then
+        look like:
+<pre>{@code @}the.pkg.Clazz::compare(ILjava/lang/String;ILjava/lang/String;)(1, 'One', 2, 'Two');
+</pre>        
+        This syntax gives enough flexibility, helps to properly select one
+        of overloaded methods and follows the tradition of previous attempts to
+        provide JavaScript to Java calling conventions.
+        <p></p>
+        Please note that to turn the special Java callback syntax on, one
+        needs to set the {@link net.java.html.js.JavaScriptBody#javacall()}
+        attribute to <b>true</b>. The callback syntax consists of
+        following parts:
+        <p></p>
+        <pre>[instance.]@classname::methodname(signature)(arguments)</pre>
+        <ul>
+            <li><b>instance</b> - must be present when calling an 
+                instance method and must be absent when calling a 
+                static method</li>
+            <li><b>classname</b> - fully qualified name of the class in 
+                which the method is declared 
+            </li>
+            <li><b>signature</b> - internal JVM method signature 
+                (as specified at 
+                <a target="top" href="http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/types.html#wp16432">JNI type Signatures</a>) 
+                without the trailing signature of the method return type</li>
+            <li><b>arguments</b> - the actual values to pass to the called Java method
+            </li>
+        </ul>
+
+        <p>Here is the <a target="top" href="http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/types.html#wp16432">JNI type signatures table</a>
+            one can use to convert
+            Java parameters to JVM's internal <em>letter</em> based 
+            representation:</p>
+        
+        <table border=1 width='100%'>
+            <tr>
+                <td><b>Type Signature</b></td>
+                <td><b>Java Type</b></td>
+            </tr>
+            <tr>
+              <td>Z</td>
+              <td>boolean</td>
+            </tr>
+            <tr>
+              <td>B</td>
+              <td>byte</td>
+            </tr>
+            <tr>
+              <td>C</td>
+              <td>char</td>
+            </tr>
+            <tr>
+              <td>S</td>
+              <td>short</td>
+            </tr>
+            <tr>
+              <td>I</td>
+              <td>int</td>
+            </tr>
+            <tr>
+              <td>J</td>
+              <td>long</td>
+            </tr>
+            <tr>
+              <td>F</td>
+              <td>float</td>
+            </tr>
+            <tr>
+              <td>D</td>
+              <td>double</td>
+            </tr>
+            <tr>
+              <td>L fully-qualified-class ;</td>
+              <td>fully-qualified-class</td>
+            </tr>
+            <tr>
+              <td>[ type</td>
+              <td>type[]</td>
+            </tr>
+          </tbody>
+        </table>
+        <p></p>
+        <em>Editing hint:</em> The callback syntax may seem complicated at
+        first, however there is an associated <b>annotation processor</b> 
+        that checks the syntax and verifies the referenced class and
+        method with the requested signature exist. If it does not, the
+        <em>compilation fails offering correct alternatives</em>. 
+        Thus don't despair seeing the syntax, make sure you get the
+        fully qualified name of the callback class right. 
+        You'll get warning and help 
+        if there is a typo in the specified signature then -
+        during compilation of your code.
+        
+        <h3>Overloaded Methods</h3>
+        
+        Specifying the actual callback signature is important in case of 
+        overloaded methods. Imagine a class:
+<pre>
+<b>package</b> x.y.z;
+<b>class</b> Handler {
+  <b>int</b> pulse() {
+    <b>return</b> 1;
+  }
+  <b>int</b> pulse(<b>int</b> howMuch) {
+    <b>return</b> howMuch;
+  }
+  <b>int</b> pulse(<b>long</b> evenMore) {
+    <b>return</b> (<b>int</b>) (5 + evenMore);
+  }
+}</pre>        
+        you then need to choose in {@link net.java.html.js.JavaScriptBody} 
+        the appropriate method to call:
+<pre>
+{@code @}{@link net.java.html.js.JavaScriptBody}(args = { "h" }, javacall = <b>true</b>, <em>// you want to process the @ syntax</em>
+  body = "<b>return</b> h.@x.y.z.Handler::pulse()() +" + <em>// the call to no argument method</em>
+    "h.@x.y.z.Handler::pulse(I)(10) +" + <em>// the call to method with integer argument</em>
+    "h.@x.y.z.Handler::pulse(J)(10);" <em>// the call to method with long argument</em>
+  )
+  <b>static native void</b> threePulsesFromJavaScript(Handler h);
+  <b>static</b> {
+    <b>assert</b> 26 == threePulsesFromJavaScript(<b>new</b> Handler());
+  }
+</pre>
+        <p>
+        To avoid ambiguity, the specification of the correct signature is 
+        required on every call. However, to simplify the development,
+        there is an annotation processor to
+        verify the signature really refers to an existing method.
+        </p>
+        
+        <h3>Arrays by Copy</h3>
+        
+        It is possible to exchange arrays between Java and JavaScript. Some
+        implementations can pass arrays by reference, however in some systems
+        this is hard to achieve. To choose the least common denominator, 
+        the TCK for behavior of {@link net.java.html.js.JavaScriptBody} requires
+        the arrays to be always transfered by a copy. As such following code:
+        <pre>
+{@code @}{@link net.java.html.js.JavaScriptBody}(args = {"arr"}, body = "arr[0] = null;")
+<b>private static native void</b> uselessModify(String[] arr);
+<b>public static void</b> main(String... args) {
+  String[] hello = { "Hello", "World!" };
+  uselessModify(arr);
+  System.out.println(arr[0] + " " + arr[1]);
+}            
+</pre>
+        will still print <em>Hello World!</em> in spite the JavaScript code
+        sets the 0-th array element to <code>null</code>. Because the array
+        is passed as a copy, such assignment has no effect on the Java array.
+        <p></p>
+        In case one needs to modify an array in a JavaScript and use its 
+        values in Java, one has to return the array back as a return value:
+        <pre>        
+{@code @}{@link net.java.html.js.JavaScriptBody}(args = {"arr"}, body = "arr[0] = 'Ahoy'; return arr;")
+<b>private static native</b> Object[] usefulModify(String[] arr);
+<b>public static void</b> main(String... args) {
+  String[] hello = { "Hello", "World!" };
+  Object[] ret = usefulModify(arr);
+  System.out.println(ret[0] + " " + ret[1]);
+}            
+</pre>
+        now the program prints <em>Ahoy World!</em> as the modified array
+        is returned back and converted (by a copy) into a Java <code>Object[]</code>
+        (but of course the <code>ret != hello</code>). Usually the copy based
+        passing of arrays works OK. It is however good to keep it in mind to 
+        avoid unwanted surprises.
+        
+        <h3>Instance Reference to JavaScript Object</h3>
+        
+        When writing wrappers around existing JavaScript libraries, it may be
+        useful to hold a reference to some JavaScript object from a Java 
+        instance and use it later.
+<pre>
+<b>class</b> WrapperAroundJsObj {
+  <b>private final</b> Object js;
+
+  WrapperAroundJsObj() {
+    js = initValue();
+  }
+
+  <b>public void</b> set(int v) {
+    setValue(js, v);
+  }
+
+  {@link net.java.html.js.JavaScriptBody @JavaScriptBody}(args = {}, body = "return { value : 0 };")
+  <b>private static native</b> Object initValue();
+
+  {@link net.java.html.js.JavaScriptBody @JavaScriptBody}(
+    args = { "js", "v" }, body = "js.value = v;", wait4js = false
+  )
+  <b>private static native void</b> setValue(Object js, int v);
+}            
+</pre>      
+        The type of the Java reference is {@link java.lang.Object}. 
+        From a Java perspective it has no additional methods or fields, however
+        its properties can be manipulated from JavaScript. Send the object back
+        to JavaScript by passing it as a parameter of some method 
+        (like the <code>setValue</code> one) and perform necessary JavaScript
+        calls or changes on it.
+
+        <h3>undefined === null</h3>
+        <a name='undefined'></a>
+
+        JavaScript recognizes two <em>empty</em> values: <code>null</code> and
+        <code>undefined</code>. Java has just <code>null</code>.
+
+        For purposes of simplicity and easier inter-operability, <code>undefined</code>
+        values returned from {@link net.java.html.js.JavaScriptBody @JavaScriptBody}
+        annotated methods are converted to <code>null</code>. In the following
+        example both methods return <code>null</code>:
+<pre>
+  {@link net.java.html.js.JavaScriptBody @JavaScriptBody}(
+    args = {}, body = "var empty = {}; return empty.x;"
+  )
+  <b>private static native</b> Object returnUndefined();
+  {@link net.java.html.js.JavaScriptBody @JavaScriptBody}(
+    args = {}, body = "var empty = {}; empty.x = null; return empty.x;"
+  )
+  <b>private static native</b> Object returnNull();
+}
+</pre>
+        This is the behavior since version 1.4.
+        
+        <h3>Post Process Classes</h3>
+        <a name="post-process"></a>
+
+        Classes with {@link net.java.html.js.JavaScriptBody} annotated methods need to
+        be post processed before they can be used - e.g. their <code>native</code>
+        body needs to be generated to call into JavaScript (btw. the code is performed
+        via {@link org.netbeans.html.boot.spi.Fn}). There are three ways
+        such post processing can happen.
+        <p></p>
+        <b>Compile time</b> processing - this is the preferred method that
+        most of the <a href="http://html.java.net">Html Java APIs</a> are using. 
+        Just include following plugin configuration into your <code>pom.xml</code>
+        and your classes will be ready for execution as soon as <em>process-classes</em>
+        <a href="http://wiki.apidesign.org/wiki/Maven">Maven</a> phase is over:
+<pre> 
+&lt;plugin&gt;
+    &lt;groupId&gt;org.netbeans.html&lt;/groupId&gt;
+    &lt;artifactId&gt;html4j-maven-plugin&lt;/artifactId&gt;
+    &lt;version&gt;${net.java.html.version}&lt;/version&gt;
+    &lt;executions&gt;
+        &lt;execution&gt;
+            &lt;id&gt;js-classes&lt;/id&gt;
+            &lt;goals&gt;
+                &lt;goal&gt;process-js-annotations&lt;/goal&gt;
+            &lt;/goals&gt;
+        &lt;/execution&gt;
+    &lt;/executions&gt;
+&lt;/plugin&gt;
+</pre>
+        This plugin works in orchestration with 
+        <a href="http://wiki.apidesign.org/wiki/AnnotationProcessor">annotation
+        processor</a> associated with {@link net.java.html.js.JavaScriptBody}
+        and {@link net.java.html.js.JavaScriptResource} - the processor creates
+        list of files that need post-processing. The 
+        <a href="http://wiki.apidesign.org/wiki/Maven">Maven</a>
+        plugin reads these files, processes classes mentioned in them and 
+        modifies (and deletes at the end) the files to not include classes
+        already processed.
+        <p></p>
+        <b>Instrumentation Agent</b> - one can do processing in runtime 
+        using JDK's {@link java.lang.instrument.ClassFileTransformer instrumentation}
+        abilities. The JAR artifact of <code>org.netbeans.html:net.java.html.boot</code>
+        contains an <code>Agent-Class</code> and <code>Premain-Class</code> 
+        definitions in its manifest. As such one can launch the Java virtual
+        machine with
+<pre>
+$ java -javaagent:jarpath=net.java.html.boot-x.y.jar
+</pre>        
+        and the runtime will take care of processing bytecode of classes 
+        not yet processed in compile time before they are loaded into the
+        virtual machine. 
+        <p></p>
+        <b>Special classloading</b> - when booting your application with
+        {@link net.java.html.boot.BrowserBuilder} there is a 3rd option of
+        processing the classes. If there are some classes not yet processed
+        (remember the files listing them generated by the 
+        <a href="http://wiki.apidesign.org/wiki/AnnotationProcessor">annotation
+        processor</a>), the {@link net.java.html.boot.BrowserBuilder#showAndWait() launching method}
+        will create a special classloader to that does the processing before
+        loading the bytecode into the virtual machine.
+        <p></p>
+        The options are rich, however to avoid any troubles (as the runtime
+        processing needs to also include <code>asm-5.0.jar</code> on application
+        classpath), it is recommended
+        to perform the <b>compile time</b> processing.
+        
+        <h3>Getting Started</h3>
+        
+        There are many ways to start developing 
+        <a href="http://html.java.net">Html for Java</a> application. 
+        However to be sure one chooses the most recent setup, it is recommended
+        to switch to good old command line and use a 
+        <a href="http://wiki.apidesign.org/wiki/Knockout4Java">Maven archetype</a>
+        associated with every version of this project. Just type:
+<pre>      
+$ mvn archetype:generate \
+ -DarchetypeGroupId=org.apidesign.html \
+ -DarchetypeArtifactId=knockout4j-archetype \
+ -DarchetypeVersion=x.y
+</pre>
+        Answer few questions (for example choose myfirstbrwsrpage as artifactId) and then you can:
+<pre>
+$ cd myfirstbrwsrpage
+$ mvn process-classes exec:java
+</pre>
+        In a few seconds (or minutes if 
+        <a href="http://wiki.apidesign.org/wiki/Maven">Maven</a>
+        decides to download the whole Internet of dependencies) you should 
+        see a sample Hello World application. It is basically composed from one 
+        Java and one HTML file:
+<pre>
+$ ls src/main/java/**/DataModel.java
+$ ls src/main/webapp/pages/index.html
+</pre>
+        Play with them, modify them and enjoy
+        <a href="http://html.java.net">Html for Java</a>!
+        
+        <a name="debugging">
+        <h3>Mixed Java/JavaScript Debugging</h3>
+        </a>
+        
+        <p>
+        The following video shows how easy it is to use 
+        NetBeans 8.0, JDK8 to debug an application that intermixes Java 
+        and JavaScript calls. One can put breakpoints into Java part, 
+        as well as JavaScript source code, inspect Java as well 
+        as JavaScript variables and switch between these two 
+        languages without any restrictions.
+        </p>
+        
+        <iframe width="420" height="315" 
+            src="http://www.youtube.com/embed/EvaTejQDRwA" 
+            frameborder="0" allowfullscreen>
+        </iframe>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/main/java/org/netbeans/html/boot/impl/FindResources.java
----------------------------------------------------------------------
diff --git a/boot/src/main/java/org/netbeans/html/boot/impl/FindResources.java b/boot/src/main/java/org/netbeans/html/boot/impl/FindResources.java
new file mode 100644
index 0000000..6dd53f2
--- /dev/null
+++ b/boot/src/main/java/org/netbeans/html/boot/impl/FindResources.java
@@ -0,0 +1,56 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.impl;
+
+import java.net.URL;
+import java.util.Collection;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public interface FindResources {
+
+    public void findResources(String path, Collection<? super URL> results, boolean oneIsEnough);
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/main/java/org/netbeans/html/boot/impl/FnContext.java
----------------------------------------------------------------------
diff --git a/boot/src/main/java/org/netbeans/html/boot/impl/FnContext.java b/boot/src/main/java/org/netbeans/html/boot/impl/FnContext.java
new file mode 100644
index 0000000..9bb1ebf
--- /dev/null
+++ b/boot/src/main/java/org/netbeans/html/boot/impl/FnContext.java
@@ -0,0 +1,165 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.impl;
+
+import java.io.BufferedReader;
+import java.io.Closeable;
+import java.io.Flushable;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.netbeans.html.boot.spi.Fn;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public final class FnContext implements Closeable {
+    private static final Logger LOG = Logger.getLogger(FnContext.class.getName());
+    private static final FnContext DUMMY;
+    static {
+        DUMMY = new FnContext(null, null);
+        DUMMY.prev = DUMMY;
+    }
+
+    public static URL isJavaScriptCapable(ClassLoader l) {
+        if (l instanceof JsClassLoader) {
+            return null;
+        }
+        return l.getResource("META-INF/net.java.html.js.classes");
+    }
+
+    public static ClassLoader newLoader(URL res, FindResources impl, Fn.Presenter p, ClassLoader parent) {
+        StringWriter w = new StringWriter();
+        PrintWriter pw = new PrintWriter(w);
+        Throwable t;
+        try {
+            Method newLoader = Class.forName("org.netbeans.html.boot.impl.FnUtils") // NOI18N
+                .getMethod("newLoader", FindResources.class, Fn.Presenter.class, ClassLoader.class);
+            return (ClassLoader) newLoader.invoke(null, impl, p, parent);
+        } catch (LinkageError ex) {
+            t = ex;
+        } catch (Exception ex) {
+            t = ex;
+        }
+        pw.println("When using @JavaScriptBody methods, one needs to either:");
+        pw.println(" - include asm-5.0.jar on runtime classpath");
+        pw.println(" - post process classes, see http://bits.netbeans.org/html+java/dev/net/java/html/js/package-summary.html#post-process");
+        pw.append("However following classes has not been processed from ").println(res);
+        
+        try {
+            BufferedReader r = new BufferedReader(new InputStreamReader(res.openStream()));
+            for (;;) {
+                String line = r.readLine();
+                if (line == null) {
+                    break;
+                }
+                pw.append("  ").println(line);
+            }
+            r.close();
+        } catch (IOException io) {
+            pw.append("Cannot read ").println(res);
+            io.printStackTrace(pw);
+        }
+        pw.println("Cannot initialize asm-5.0.jar!");
+        pw.flush();
+        LOG.log(Level.SEVERE, w.toString(), t);
+        return null;
+    }
+
+    private Object prev;
+    private final Fn.Presenter current;
+    private FnContext(Fn.Presenter prevP, Fn.Presenter newP) {
+        this.current = newP;
+        this.prev = prevP;
+    }
+
+    @Override
+    public void close() throws IOException {
+        if (prev != this) {
+            currentPresenter((Fn.Presenter)prev);
+            prev = this;
+            if (current instanceof Flushable) {
+                ((Flushable)current).flush();
+            }
+        }
+    }
+/*
+    @Override
+    protected void finalize() throws Throwable {
+        if (prev != null) {
+            LOG.warning("Unclosed context!");
+        }
+    }
+*/
+    public static Closeable activate(Fn.Presenter newP) {
+        final Fn.Presenter oldP = currentPresenter(newP);
+        if (oldP == newP) {
+            return DUMMY;
+        }
+        return new FnContext(oldP, newP);
+    }
+    
+    
+    private static final ThreadLocal<Fn.Presenter> CURRENT = new ThreadLocal<Fn.Presenter>();
+
+    public static Fn.Presenter currentPresenter(Fn.Presenter p) {
+        Fn.Presenter prev = CURRENT.get();
+        CURRENT.set(p);
+        return prev;
+    }
+
+    public static Fn.Presenter currentPresenter(boolean fail) {
+        Fn.Presenter p = CURRENT.get();
+        if (p == null && fail) {
+            throw new IllegalStateException("No current WebView context around!");
+        }
+        return p;
+    }
+    
+}


[21/24] incubator-netbeans-html4j git commit: [INFRA-15006] Initial donation of HTML/Java NetBeans API. Equivalent to the content of html4j-donation-review.zip donated as part of ApacheNetBeansDonation1.zip with SHA256 being 7f2ca0f61953a190613c9a0fbcc1b

Posted by jt...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-script/src/test/java/net/java/html/boot/script/ko4j/DynamicHTTP.java
----------------------------------------------------------------------
diff --git a/boot-script/src/test/java/net/java/html/boot/script/ko4j/DynamicHTTP.java b/boot-script/src/test/java/net/java/html/boot/script/ko4j/DynamicHTTP.java
new file mode 100644
index 0000000..a408c3b
--- /dev/null
+++ b/boot-script/src/test/java/net/java/html/boot/script/ko4j/DynamicHTTP.java
@@ -0,0 +1,261 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.boot.script.ko4j;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.glassfish.grizzly.PortRange;
+import org.glassfish.grizzly.http.server.HttpHandler;
+import org.glassfish.grizzly.http.server.HttpServer;
+import org.glassfish.grizzly.http.server.NetworkListener;
+import org.glassfish.grizzly.http.server.Request;
+import org.glassfish.grizzly.http.server.Response;
+import org.glassfish.grizzly.http.server.ServerConfiguration;
+import org.glassfish.grizzly.websockets.WebSocket;
+import org.glassfish.grizzly.websockets.WebSocketAddOn;
+import org.glassfish.grizzly.websockets.WebSocketApplication;
+import org.glassfish.grizzly.websockets.WebSocketEngine;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+final class DynamicHTTP extends HttpHandler {
+    private static final Logger LOG = Logger.getLogger(DynamicHTTP.class.getName());
+    private static int resourcesCount;
+    private static List<Resource> resources;
+    private static ServerConfiguration conf;
+    private static HttpServer server;
+
+    private DynamicHTTP() {
+    }
+
+    static URI initServer() throws Exception {
+        server = HttpServer.createSimpleServer(null, new PortRange(8080, 65535));
+        final WebSocketAddOn addon = new WebSocketAddOn();
+        for (NetworkListener listener : server.getListeners()) {
+            listener.registerAddOn(addon);
+        }
+        resources = new ArrayList<Resource>();
+
+        conf = server.getServerConfiguration();
+        final DynamicHTTP dh = new DynamicHTTP();
+
+        conf.addHttpHandler(dh, "/");
+
+        server.start();
+
+        return pageURL("http", server, "/test.html");
+    }
+
+    @Override
+    public void service(Request request, Response response) throws Exception {
+        if ("/test.html".equals(request.getRequestURI())) {
+            response.setContentType("text/html");
+            final InputStream is = DynamicHTTP.class.getResourceAsStream("test.html");
+            copyStream(is, response.getOutputStream(), null);
+            return;
+        }
+        if ("/dynamic".equals(request.getRequestURI())) {
+            String mimeType = request.getParameter("mimeType");
+            List<String> params = new ArrayList<String>();
+            boolean webSocket = false;
+            for (int i = 0;; i++) {
+                String p = request.getParameter("param" + i);
+                if (p == null) {
+                    break;
+                }
+                if ("protocol:ws".equals(p)) {
+                    webSocket = true;
+                    continue;
+                }
+                params.add(p);
+            }
+            final String cnt = request.getParameter("content");
+            String mangle = cnt.replace("%20", " ").replace("%0A", "\n");
+            ByteArrayInputStream is = new ByteArrayInputStream(mangle.getBytes("UTF-8"));
+            URI url;
+            final Resource res = new Resource(is, mimeType, "/dynamic/res" + ++resourcesCount, params.toArray(new String[params.size()]));
+            if (webSocket) {
+                url = registerWebSocket(res);
+            } else {
+                url = registerResource(res);
+            }
+            response.getWriter().write(url.toString());
+            response.getWriter().write("\n");
+            return;
+        }
+
+        for (Resource r : resources) {
+            if (r.httpPath.equals(request.getRequestURI())) {
+                response.setContentType(r.httpType);
+                r.httpContent.reset();
+                String[] params = null;
+                if (r.parameters.length != 0) {
+                    params = new String[r.parameters.length];
+                    for (int i = 0; i < r.parameters.length; i++) {
+                        params[i] = request.getParameter(r.parameters[i]);
+                        if (params[i] == null) {
+                            if ("http.method".equals(r.parameters[i])) {
+                                params[i] = request.getMethod().toString();
+                            } else if ("http.requestBody".equals(r.parameters[i])) {
+                                Reader rdr = request.getReader();
+                                StringBuilder sb = new StringBuilder();
+                                for (;;) {
+                                    int ch = rdr.read();
+                                    if (ch == -1) {
+                                        break;
+                                    }
+                                    sb.append((char) ch);
+                                }
+                                params[i] = sb.toString();
+                            } else if (r.parameters[i].startsWith("http.header.")) {
+                                params[i] = request.getHeader(r.parameters[i].substring(12));
+                            }
+                        }
+                        if (params[i] == null) {
+                            params[i] = "null";
+                        }
+                    }
+                }
+
+                copyStream(r.httpContent, response.getOutputStream(), null, params);
+            }
+        }
+    }
+
+    private URI registerWebSocket(Resource r) {
+        WebSocketEngine.getEngine().register("", r.httpPath, new WS(r));
+        return pageURL("ws", server, r.httpPath);
+    }
+
+    private URI registerResource(Resource r) {
+        if (!resources.contains(r)) {
+            resources.add(r);
+            conf.addHttpHandler(this, r.httpPath);
+        }
+        return pageURL("http", server, r.httpPath);
+    }
+
+    private static URI pageURL(String proto, HttpServer server, final String page) {
+        NetworkListener listener = server.getListeners().iterator().next();
+        int port = listener.getPort();
+        try {
+            return new URI(proto + "://localhost:" + port + page);
+        } catch (URISyntaxException ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+
+    static final class Resource {
+
+        final InputStream httpContent;
+        final String httpType;
+        final String httpPath;
+        final String[] parameters;
+
+        Resource(InputStream httpContent, String httpType, String httpPath,
+            String[] parameters) {
+            httpContent.mark(Integer.MAX_VALUE);
+            this.httpContent = httpContent;
+            this.httpType = httpType;
+            this.httpPath = httpPath;
+            this.parameters = parameters;
+        }
+    }
+
+    static void copyStream(InputStream is, OutputStream os, String baseURL, String... params) throws IOException {
+        for (;;) {
+            int ch = is.read();
+            if (ch == -1) {
+                break;
+            }
+            if (ch == '$' && params.length > 0) {
+                int cnt = is.read() - '0';
+                if (baseURL != null && cnt == 'U' - '0') {
+                    os.write(baseURL.getBytes("UTF-8"));
+                } else {
+                    if (cnt >= 0 && cnt < params.length) {
+                        os.write(params[cnt].getBytes("UTF-8"));
+                    } else {
+                        os.write('$');
+                        os.write(cnt + '0');
+                    }
+                }
+            } else {
+                os.write(ch);
+            }
+        }
+    }
+
+    private static class WS extends WebSocketApplication {
+        private final Resource r;
+
+        private WS(Resource r) {
+            this.r = r;
+        }
+
+        @Override
+        public void onMessage(WebSocket socket, String text) {
+            try {
+                r.httpContent.reset();
+                ByteArrayOutputStream out = new ByteArrayOutputStream();
+                copyStream(r.httpContent, out, null, text);
+                String s = new String(out.toByteArray(), "UTF-8");
+                socket.send(s);
+            } catch (IOException ex) {
+                LOG.log(Level.WARNING, "Error processing message " + text, ex);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-script/src/test/java/net/java/html/boot/script/ko4j/KOCase.java
----------------------------------------------------------------------
diff --git a/boot-script/src/test/java/net/java/html/boot/script/ko4j/KOCase.java b/boot-script/src/test/java/net/java/html/boot/script/ko4j/KOCase.java
new file mode 100644
index 0000000..0498ea8
--- /dev/null
+++ b/boot-script/src/test/java/net/java/html/boot/script/ko4j/KOCase.java
@@ -0,0 +1,140 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.boot.script.ko4j;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.netbeans.html.boot.spi.Fn;
+import org.testng.ITest;
+import org.testng.SkipException;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public final class KOCase implements ITest, Runnable {
+    static final Executor JS = Executors.newSingleThreadExecutor();
+    private final Fn.Presenter p;
+    private final Method m;
+    private final String skipMsg;
+    private Object result;
+    private Object inst;
+    private int count;
+
+    KOCase(Fn.Presenter p, Method m, String skipMsg) {
+        this.p = p;
+        this.m = m;
+        this.skipMsg = skipMsg;
+    }
+
+    @Override
+    public String getTestName() {
+        return m.getName();
+    }
+
+    @Test
+    public synchronized void executeTest() throws Exception {
+        if (skipMsg != null) {
+            throw new SkipException(skipMsg);
+        }
+        if (result == null) {
+            JS.execute(this);
+            wait();
+        }
+        if (result instanceof Exception) {
+            throw (Exception)result;
+        }
+        if (result instanceof Error) {
+            throw (Error)result;
+        }
+    }
+
+    @Override
+    public synchronized void run() {
+        boolean notify = true;
+        Closeable a = Fn.activate(p);
+        try {
+            if (inst == null) {
+                inst = m.getDeclaringClass().newInstance();
+            }
+            result = m.invoke(inst);
+            if (result == null) {
+                result = this;
+            }
+        } catch (InvocationTargetException ex) {
+            Throwable r = ex.getTargetException();
+            if (r instanceof InterruptedException) {
+                if (count++ < 1000) {
+                    notify = false;
+                    try {
+                        Thread.sleep(30);
+                    } catch (InterruptedException ignore) {
+                        // just go on
+                    }
+                    JS.execute(this);
+                    return;
+                }
+            }
+            result = r;
+        } catch (Exception ex) {
+            result = ex;
+        } finally {
+            if (notify) {
+                notifyAll();
+            }
+            try {
+                a.close();
+            } catch (IOException ex) {
+                throw new IllegalStateException(ex);
+            }
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-script/src/test/java/net/java/html/boot/script/ko4j/KnockoutEnvJSTest.java
----------------------------------------------------------------------
diff --git a/boot-script/src/test/java/net/java/html/boot/script/ko4j/KnockoutEnvJSTest.java b/boot-script/src/test/java/net/java/html/boot/script/ko4j/KnockoutEnvJSTest.java
new file mode 100644
index 0000000..8ed88c5
--- /dev/null
+++ b/boot-script/src/test/java/net/java/html/boot/script/ko4j/KnockoutEnvJSTest.java
@@ -0,0 +1,266 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.boot.script.ko4j;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import net.java.html.BrwsrCtx;
+import net.java.html.boot.BrowserBuilder;
+import net.java.html.boot.script.Scripts;
+import net.java.html.js.JavaScriptBody;
+import org.netbeans.html.boot.spi.Fn;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.json.spi.Technology;
+import org.netbeans.html.json.spi.Transfer;
+import org.netbeans.html.json.tck.KOTest;
+import org.netbeans.html.json.tck.KnockoutTCK;
+import org.netbeans.html.ko4j.KO4J;
+import org.netbeans.html.wstyrus.TyrusContext;
+import org.openide.util.lookup.ServiceProvider;
+import org.testng.Assert;
+import static org.testng.Assert.*;
+import org.testng.annotations.Factory;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+@ServiceProvider(service = KnockoutTCK.class)
+public final class KnockoutEnvJSTest extends KnockoutTCK {
+    private static Class<?> browserClass;
+    private static Fn.Presenter browserContext;
+    private static URI baseUri;
+
+    public KnockoutEnvJSTest() {
+    }
+
+    @Factory public static Object[] compatibilityTests() throws Exception {
+        try {
+            Class.forName("java.lang.FunctionalInterface");
+        } catch (ClassNotFoundException ex) {
+            // only runs on JDK8
+            return new Object[0];
+        }
+
+
+        Class[] arr = testClasses();
+        for (int i = 0; i < arr.length; i++) {
+            assertEquals(
+                arr[i].getClassLoader(),
+                KnockoutEnvJSTest.class.getClassLoader(),
+                "All classes loaded by the same classloader"
+            );
+        }
+
+        baseUri = DynamicHTTP.initServer();
+
+        final Fn.Presenter p = Scripts.createPresenter(KOCase.JS);
+        URL envNashorn = new URL("https://bugs.openjdk.java.net/secure/attachment/11894/env.nashorn.1.2-debug.js");
+        InputStream is = envNashorn.openStream();
+        p.loadScript(new InputStreamReader(is));
+        is.close();
+
+        final BrowserBuilder bb = BrowserBuilder.newBrowser(p).
+            loadClass(KnockoutEnvJSTest.class).
+            loadPage(baseUri.toString()).
+            invoke("initialized");
+
+        Executors.newSingleThreadExecutor().submit(new Runnable() {
+            @Override
+            public void run() {
+                bb.showAndWait();
+            }
+        });
+
+        ClassLoader l = getClassLoader();
+        List<Object> res = new ArrayList<Object>();
+        for (int i = 0; i < arr.length; i++) {
+            Class<?> c = Class.forName(arr[i].getName(), true, l);
+            seekKOTests(c, res);
+        }
+        return res.toArray();
+    }
+
+    private static void seekKOTests(Class<?> c, List<Object> res) throws SecurityException, ClassNotFoundException {
+        Class<? extends Annotation> koTest =
+            c.getClassLoader().loadClass(KOTest.class.getName()).
+            asSubclass(Annotation.class);
+        for (Method m : c.getMethods()) {
+            if (m.getAnnotation(koTest) != null) {
+                res.add(new KOCase(browserContext, m, skipMsg(m.getName())));
+            }
+        }
+    }
+
+    private static String skipMsg(String methodName) {
+        final String ver = System.getProperty("java.runtime.version"); // NOI18N
+        if (
+            ver.startsWith("1.8.0_25") ||
+            ver.startsWith("1.8.0_40")
+        ) {
+            return "Broken due to JDK-8047764";
+        }
+        if (
+            !ver.startsWith("1.8.0_")
+        ) {
+            // 1.8.0_ are and will remain broken
+            return null;
+        }
+        switch (methodName) {
+            case "paintTheGridOnClick":
+            case "displayContentOfArrayOfPeople":
+            case "connectUsingWebSocket":
+            case "selectWorksOnModels":
+            case "archetypeArrayModificationVisible":
+            case "noLongerNeededArrayElementsCanDisappear":
+                return "Does not work on JDK8, due to JDK-8046013";
+            case "modifyRadioValueOnEnum":
+                return "Does not work on JDK8";
+        }
+        return null;
+    }
+
+    static synchronized ClassLoader getClassLoader() throws InterruptedException {
+        while (browserClass == null) {
+            KnockoutEnvJSTest.class.wait();
+        }
+        return browserClass.getClassLoader();
+    }
+
+    public static synchronized void initialized(Class<?> browserCls) throws Exception {
+        browserClass = browserCls;
+        browserContext = Fn.activePresenter();
+        KnockoutEnvJSTest.class.notifyAll();
+    }
+
+    public static void initialized() throws Exception {
+        Assert.assertSame(
+            KnockoutEnvJSTest.class.getClassLoader(),
+            ClassLoader.getSystemClassLoader(),
+            "No special classloaders"
+        );
+        KnockoutEnvJSTest.initialized(KnockoutEnvJSTest.class);
+        browserContext = Fn.activePresenter();
+    }
+
+    @Override
+    public BrwsrCtx createContext() {
+        KO4J fx = new KO4J(browserContext);
+        TyrusContext tc = new TyrusContext();
+        Contexts.Builder cb = Contexts.newBuilder().
+            register(Technology.class, fx.knockout(), 10).
+            register(Transfer.class, tc, 10);
+        cb.register(Fn.Presenter.class, browserContext, 10);
+        cb.register(Executor.class, (Executor)browserContext, 10);
+        BrwsrCtx ctx = cb.build();
+        return ctx;
+    }
+
+    @Override
+    public Object createJSON(Map<String, Object> values) {
+        Object json = createJSON();
+        for (Map.Entry<String, Object> entry : values.entrySet()) {
+            setProperty(json, entry.getKey(), entry.getValue());
+        }
+        return json;
+    }
+
+    @JavaScriptBody(args = {}, body = "return new Object();")
+    private static native Object createJSON();
+    @JavaScriptBody(args = { "json", "key", "value" }, body = "json[key] = value;")
+    private static native void setProperty(Object json, String key, Object value);
+
+    @Override
+    @JavaScriptBody(args = { "s", "args" }, body = "\n"
+        + "var f = new Function(s);\n"
+        + "return f.apply(null, args);\n"
+    )
+    public native Object executeScript(String script, Object[] arguments);
+
+    private static String findBaseURL() {
+        return baseUri.toString();
+    }
+
+    @Override
+    public URI prepareURL(String content, String mimeType, String[] parameters) {
+        try {
+            final URL baseURL = new URL(findBaseURL());
+            StringBuilder sb = new StringBuilder();
+            sb.append("/dynamic?mimeType=").append(mimeType);
+            for (int i = 0; i < parameters.length; i++) {
+                sb.append("&param" + i).append("=").append(parameters[i]);
+            }
+            String mangle = content.replace("\n", "%0a")
+                .replace("\"", "\\\"").replace(" ", "%20");
+            sb.append("&content=").append(mangle);
+
+            URL query = new URL(baseURL, sb.toString());
+            URLConnection c = query.openConnection();
+            BufferedReader br = new BufferedReader(new InputStreamReader(c.getInputStream()));
+            URI connectTo = new URI(br.readLine());
+            return connectTo;
+        } catch (IOException ex) {
+            throw new IllegalStateException(ex);
+        } catch (URISyntaxException ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+
+    @Override
+    public boolean canFailWebSocketTest() {
+        return true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-script/src/test/resources/net/java/html/boot/script/ko4j/test.html
----------------------------------------------------------------------
diff --git a/boot-script/src/test/resources/net/java/html/boot/script/ko4j/test.html b/boot-script/src/test/resources/net/java/html/boot/script/ko4j/test.html
new file mode 100644
index 0000000..af8ad2e
--- /dev/null
+++ b/boot-script/src/test/resources/net/java/html/boot/script/ko4j/test.html
@@ -0,0 +1,56 @@
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>Knockout.fx Execution Harness</title>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <meta name="viewport" content="width=device-width">
+    </head>
+    <body>
+        <h1>Knockout in Env.Execution Harness</h1>
+        <script></script>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-truffle/empty.sigtest
----------------------------------------------------------------------
diff --git a/boot-truffle/empty.sigtest b/boot-truffle/empty.sigtest
new file mode 100644
index 0000000..42942c0
--- /dev/null
+++ b/boot-truffle/empty.sigtest
@@ -0,0 +1,21 @@
+#Signature file v4.1
+#Version 2.0-SNAPSHOT
+
+CLSS public java.lang.Object
+cons public init()
+meth protected java.lang.Object clone() throws java.lang.CloneNotSupportedException
+meth protected void finalize() throws java.lang.Throwable
+meth public boolean equals(java.lang.Object)
+meth public final java.lang.Class<?> getClass()
+meth public final void notify()
+meth public final void notifyAll()
+meth public final void wait() throws java.lang.InterruptedException
+meth public final void wait(long) throws java.lang.InterruptedException
+meth public final void wait(long,int) throws java.lang.InterruptedException
+meth public int hashCode()
+meth public java.lang.String toString()
+
+CLSS public final net.java.html.boot.truffle.TrufflePresenters
+meth public static org.netbeans.html.boot.spi.Fn$Presenter create(java.util.concurrent.Executor)
+supr java.lang.Object
+

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-truffle/pom.xml
----------------------------------------------------------------------
diff --git a/boot-truffle/pom.xml b/boot-truffle/pom.xml
new file mode 100644
index 0000000..912aca0
--- /dev/null
+++ b/boot-truffle/pom.xml
@@ -0,0 +1,209 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2016 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<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">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.netbeans.html</groupId>
+        <artifactId>pom</artifactId>
+        <version>2.0-SNAPSHOT</version>
+    </parent>
+    <name>Presenter via Truffle</name>
+    <artifactId>net.java.html.boot.truffle</artifactId>
+    <version>2.0-SNAPSHOT</version>
+    <packaging>bundle</packaging>
+    <properties>
+        <netbeans.compile.on.save>NONE</netbeans.compile.on.save>
+        <publicPackages>net.java.html.boot.truffle</publicPackages>
+        <trufflejs>${java.home}/language/js/trufflejs.jar</trufflejs>
+        <skipTests>true</skipTests>
+    </properties>
+    <build>
+      <plugins>
+          <plugin>
+              <groupId>org.apache.felix</groupId>
+              <artifactId>maven-bundle-plugin</artifactId>
+              <extensions>true</extensions>
+              <configuration>
+                  <instructions>
+                      <Export-Package>${publicPackages}</Export-Package>
+                      <Bundle-SymbolicName>net.java.html.boot.truffle</Bundle-SymbolicName>
+                  </instructions>
+              </configuration>
+          </plugin>
+          <plugin>
+              <groupId>org.netbeans.html</groupId>
+              <artifactId>html4j-maven-plugin</artifactId>
+          </plugin>
+         <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-compiler-plugin</artifactId>
+            <version>2.3.2</version>
+            <configuration>
+               <source>1.7</source>
+               <target>1.7</target>
+            </configuration>
+         </plugin>
+         <plugin>
+             <groupId>org.netbeans.tools</groupId>
+             <artifactId>sigtest-maven-plugin</artifactId>
+             <version>1.0</version>
+             <executions>
+                 <execution>
+                     <goals>
+                         <goal>generate</goal>
+                         <goal>check</goal>
+                     </goals>
+                 </execution>
+             </executions>
+             <configuration>
+                 <sigfile>empty.sigtest</sigfile>
+             </configuration>
+         </plugin>
+      </plugins>
+    </build>
+    <dependencies>
+        <dependency>
+            <groupId>org.netbeans.api</groupId>
+            <artifactId>org-openide-util-lookup</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.oracle.truffle</groupId>
+            <artifactId>truffle-api</artifactId>
+            <version>0.18</version>
+        </dependency>
+        <dependency>
+            <groupId>com.oracle.truffle</groupId>
+            <artifactId>truffle-dsl-processor</artifactId>
+            <version>0.18</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.netbeans.html</groupId>
+            <artifactId>net.java.html.boot</artifactId>
+            <version>${project.version}</version>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>org.testng</groupId>
+            <artifactId>testng</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>net.java.html.json.tck</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.grizzly</groupId>
+            <artifactId>grizzly-http-server</artifactId>
+            <version>${grizzly.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.grizzly</groupId>
+            <artifactId>grizzly-websockets-server</artifactId>
+            <version>${grizzly.version}</version>
+            <scope>test</scope>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.grizzly</groupId>
+            <artifactId>grizzly-http-servlet</artifactId>
+            <version>${grizzly.version}</version>
+            <scope>test</scope>
+        </dependency>    
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <scope>test</scope>
+            <version>3.1.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.netbeans.html</groupId>
+            <artifactId>ko4j</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ko-ws-tyrus</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>com.dukescript.libraries</groupId>
+            <artifactId>net.java.html.lib</artifactId>
+            <version>0.3</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    <profiles>
+        <profile>
+            <id>graalvm</id>
+            <activation>
+                <file>
+                    <exists>${trufflejs}</exists>
+                </file>
+            </activation>
+            <properties>
+                <skipTests>false</skipTests>
+            </properties>
+            <dependencies>
+                <dependency>
+                    <groupId>com.oracle.graaljs</groupId>
+                    <artifactId>truffle-js</artifactId>
+                    <version>0.07</version>
+                    <systemPath>${trufflejs}</systemPath>
+                    <scope>system</scope>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-truffle/src/main/java/net/java/html/boot/truffle/IsArrayNode.java
----------------------------------------------------------------------
diff --git a/boot-truffle/src/main/java/net/java/html/boot/truffle/IsArrayNode.java b/boot-truffle/src/main/java/net/java/html/boot/truffle/IsArrayNode.java
new file mode 100644
index 0000000..a4e8913
--- /dev/null
+++ b/boot-truffle/src/main/java/net/java/html/boot/truffle/IsArrayNode.java
@@ -0,0 +1,68 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.boot.truffle;
+
+import com.oracle.truffle.api.TruffleLanguage;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.Message;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.RootNode;
+
+final class IsArrayNode extends RootNode {
+    @Child
+    private Node check;
+
+    IsArrayNode() {
+        super(TruffleLanguage.class, null, null);
+        this.check = Message.HAS_SIZE.createNode();
+    }
+
+    @Override
+    public Object execute(VirtualFrame frame) {
+        final Object[] args = frame.getArguments();
+        Object result = ForeignAccess.sendHasSize(check, frame, (TruffleObject) args[0]);
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-truffle/src/main/java/net/java/html/boot/truffle/IsNullNode.java
----------------------------------------------------------------------
diff --git a/boot-truffle/src/main/java/net/java/html/boot/truffle/IsNullNode.java b/boot-truffle/src/main/java/net/java/html/boot/truffle/IsNullNode.java
new file mode 100644
index 0000000..250ae42
--- /dev/null
+++ b/boot-truffle/src/main/java/net/java/html/boot/truffle/IsNullNode.java
@@ -0,0 +1,68 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.boot.truffle;
+
+import com.oracle.truffle.api.TruffleLanguage;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.Message;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.RootNode;
+
+final class IsNullNode extends RootNode {
+    @Child
+    private Node check;
+
+    IsNullNode() {
+        super(TruffleLanguage.class, null, null);
+        this.check = Message.IS_NULL.createNode();
+    }
+
+    @Override
+    public Object execute(VirtualFrame frame) {
+        final Object[] args = frame.getArguments();
+        Object result = ForeignAccess.sendIsNull(check, frame, (TruffleObject) args[0]);
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-truffle/src/main/java/net/java/html/boot/truffle/JavaArray.java
----------------------------------------------------------------------
diff --git a/boot-truffle/src/main/java/net/java/html/boot/truffle/JavaArray.java b/boot-truffle/src/main/java/net/java/html/boot/truffle/JavaArray.java
new file mode 100644
index 0000000..2e7c8a1
--- /dev/null
+++ b/boot-truffle/src/main/java/net/java/html/boot/truffle/JavaArray.java
@@ -0,0 +1,102 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.boot.truffle;
+
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.MessageResolution;
+import com.oracle.truffle.api.interop.Resolve;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.nodes.Node;
+import java.lang.reflect.Array;
+
+@MessageResolution(receiverType = JavaArray.class, language = TrufflePresenter.JavaLang.class)
+final class JavaArray extends JavaValue implements TruffleObject {
+    final TrufflePresenter.WrapArray wrap;
+    final Object arr;
+
+    public JavaArray(TrufflePresenter.WrapArray wrap, Object arr) {
+        this.arr = arr;
+        this.wrap = wrap;
+    }
+
+    @Override
+    public ForeignAccess getForeignAccess() {
+        return JavaArrayForeign.ACCESS;
+    }
+
+    @Override
+    public Object get() {
+        return arr;
+    }
+
+    static boolean isInstance(TruffleObject obj) {
+        return obj instanceof JavaArray;
+    }
+
+    static boolean isArray(Object obj) {
+        return obj != null && obj.getClass().getComponentType() != null;
+    }
+
+    @Resolve(message = "READ")
+    static abstract class ReadNode extends Node {
+        protected Object access(JavaArray arr, int index) {
+            Object obj = Array.get(arr.arr, index);
+            return toJavaScript(obj, arr.wrap);
+        }
+    }
+
+    @Resolve(message = "HAS_SIZE")
+    static abstract class HasSizeNode extends Node {
+        protected boolean access(JavaArray arr) {
+            return true;
+        }
+    }
+
+    @Resolve(message = "GET_SIZE")
+    static abstract class GetSizeNode extends Node {
+        protected int access(JavaArray arr) {
+            return Array.getLength(arr.arr);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-truffle/src/main/java/net/java/html/boot/truffle/JavaObject.java
----------------------------------------------------------------------
diff --git a/boot-truffle/src/main/java/net/java/html/boot/truffle/JavaObject.java b/boot-truffle/src/main/java/net/java/html/boot/truffle/JavaObject.java
new file mode 100644
index 0000000..234f396
--- /dev/null
+++ b/boot-truffle/src/main/java/net/java/html/boot/truffle/JavaObject.java
@@ -0,0 +1,82 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.boot.truffle;
+
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.MessageResolution;
+import com.oracle.truffle.api.interop.Resolve;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.nodes.Node;
+
+@MessageResolution(receiverType = JavaObject.class, language = TrufflePresenter.JavaLang.class)
+final class JavaObject extends JavaValue implements TruffleObject {
+    final Object obj;
+
+    JavaObject(Object obj) {
+        this.obj = obj;
+    }
+
+
+    @Override
+    public ForeignAccess getForeignAccess() {
+        return JavaObjectForeign.ACCESS;
+    }
+
+    public static boolean isInstance(TruffleObject obj) {
+        return obj instanceof JavaObject;
+    }
+
+    @Override
+    public Object get() {
+        return obj;
+    }
+
+    @Resolve(message = "HAS_SIZE")
+    static abstract class NoSizeNode extends Node {
+
+        protected boolean access(JavaObject obj) {
+            return false;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-truffle/src/main/java/net/java/html/boot/truffle/JavaValue.java
----------------------------------------------------------------------
diff --git a/boot-truffle/src/main/java/net/java/html/boot/truffle/JavaValue.java b/boot-truffle/src/main/java/net/java/html/boot/truffle/JavaValue.java
new file mode 100644
index 0000000..94bba23
--- /dev/null
+++ b/boot-truffle/src/main/java/net/java/html/boot/truffle/JavaValue.java
@@ -0,0 +1,91 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.boot.truffle;
+
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.interop.java.JavaInterop;
+
+abstract class JavaValue {
+    public abstract Object get();
+
+    static Object toJavaScript(Object conv, TrufflePresenter.WrapArray wrap) {
+        if (conv instanceof TruffleObject) {
+            return conv;
+        }
+        if (JavaArray.isArray(conv)) {
+            conv = wrap.copy(new JavaArray(wrap, conv));
+        }
+        if (conv instanceof Character) {
+            conv = (int) (Character) conv;
+        }
+        if (conv == null || conv.getClass().getName().endsWith(".$JsCallbacks$")) { // NOI18N
+            conv = JavaInterop.asTruffleObject(conv);
+        } else if (!isJSReady(conv)) {
+            conv = new JavaObject(conv);
+        }
+        return conv;
+    }
+
+    private static boolean isJSReady(Object obj) {
+        if (obj == null) {
+            return true;
+        }
+        if (obj instanceof String) {
+            return true;
+        }
+        if (obj instanceof Number) {
+            return true;
+        }
+        if (obj instanceof Character) {
+            return true;
+        }
+        if (obj instanceof Boolean) {
+            return true;
+        }
+        if (obj instanceof TruffleObject) {
+            return true;
+        }
+        return false;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-truffle/src/main/java/net/java/html/boot/truffle/TrufflePresenter.java
----------------------------------------------------------------------
diff --git a/boot-truffle/src/main/java/net/java/html/boot/truffle/TrufflePresenter.java b/boot-truffle/src/main/java/net/java/html/boot/truffle/TrufflePresenter.java
new file mode 100644
index 0000000..7d75efb
--- /dev/null
+++ b/boot-truffle/src/main/java/net/java/html/boot/truffle/TrufflePresenter.java
@@ -0,0 +1,293 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.boot.truffle;
+
+import com.oracle.truffle.api.CallTarget;
+import com.oracle.truffle.api.Truffle;
+import com.oracle.truffle.api.TruffleLanguage;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.interop.java.JavaInterop;
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.vm.PolyglotEngine;
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.Reader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+import org.netbeans.html.boot.spi.Fn;
+import org.netbeans.html.boot.spi.Fn.Presenter;
+
+/**
+ * Implementation of {@link Presenter} that delegates to Truffle.
+ *
+ * @author Jaroslav Tulach
+ */
+final class TrufflePresenter implements Fn.KeepAlive,
+    Presenter, Fn.FromJavaScript, Fn.ToJavaScript, Executor {
+
+    private Eval eval;
+    private WrapArray copy;
+    private final Executor exc;
+    private final CallTarget isNull;
+    private final CallTarget isArray;
+    private Apply apply;
+    private TruffleObject jsNull;
+
+    TrufflePresenter(Executor exc, TruffleObject eval) {
+        this.exc = exc;
+        this.eval = eval == null ? null : JavaInterop.asJavaFunction(Eval.class, eval);
+        this.isNull = Truffle.getRuntime().createCallTarget(new IsNullNode());
+        this.isArray = Truffle.getRuntime().createCallTarget(new IsArrayNode());
+    }
+
+    @Override
+    public Fn defineFn(String code, String... names) {
+        return defineImpl(code, names, null);
+    }
+
+    @Override
+    public Fn defineFn(String code, String[] names, boolean[] keepAlive) {
+        return defineImpl(code, names, keepAlive);
+    }
+
+    private FnImpl defineImpl(String code, String[] names, boolean[] keepAlive) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("(function() {\n");
+        sb.append("  var args = Array.prototype.slice.call(arguments);\n");
+        sb.append("  var thiz = args.shift();\n");
+        sb.append("  return (function(");
+        String sep = "";
+        for (String n : names) {
+            sb.append(sep).append(n);
+            sep = ",";
+        }
+        sb.append(") {\n");
+        sb.append(code);
+        sb.append("\n  }).apply(thiz, args);\n");
+        sb.append("})\n");
+
+        TruffleObject fn = (TruffleObject) getEval().eval(sb.toString());
+        return new FnImpl(this, fn, names.length);
+    }
+
+    @Override
+    public void displayPage(URL page, Runnable onPageLoad) {
+        if (onPageLoad != null) {
+            onPageLoad.run();
+        }
+    }
+
+    @Override
+    public void loadScript(Reader code) throws Exception {
+        Source src = Source.newBuilder(code).
+            name("unknown.js").
+            mimeType("text/javascript").
+            build();
+        getEval().eval(src.getCode());
+    }
+
+    interface Apply {
+        public Object apply(Object... args);
+    }
+
+    interface WrapArray {
+        public Object copy(Object arr);
+    }
+
+    interface Eval {
+        public Object eval(String code);
+    }
+
+    final Object checkArray(Object val) throws Exception {
+        if (val instanceof TruffleObject) {
+            final TruffleObject truffleObj = (TruffleObject)val;
+            boolean hasSize = (boolean) isArray.call(truffleObj);
+            if (hasSize) {
+                List<?> list = JavaInterop.asJavaObject(List.class, truffleObj);
+                Object[] arr = list.toArray();
+                for (int i = 0; i < arr.length; i++) {
+                    arr[i] = toJava(arr[i]);
+                }
+                return arr;
+            }
+        }
+        return val;
+    }
+
+    @Override
+    public Object toJava(Object jsArray) {
+        if (jsArray instanceof JavaValue) {
+            jsArray = ((JavaValue) jsArray).get();
+        }
+        if (jsArray instanceof TruffleObject) {
+            boolean checkNull = (boolean) isNull.call(jsArray);
+            if (checkNull) {
+                return null;
+            }
+        }
+        try {
+            return checkArray(jsArray);
+        } catch (Exception ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+
+    @Override
+    public Object toJavaScript(Object conv) {
+        return JavaValue.toJavaScript(conv, getWrap());
+    }
+
+    @Override
+    public void execute(final Runnable command) {
+        if (Fn.activePresenter() == this) {
+            command.run();
+            return;
+        }
+
+        class Wrap implements Runnable {
+
+            @Override
+            public void run() {
+                try (Closeable c = Fn.activate(TrufflePresenter.this)) {
+                    command.run();
+                } catch (IOException ex) {
+                    throw new IllegalStateException(ex);
+                }
+            }
+        }
+        final Runnable wrap = new Wrap();
+        if (exc == null) {
+            wrap.run();
+        } else {
+            exc.execute(wrap);
+        }
+    }
+
+    private Eval getEval() {
+        if (eval == null) {
+            try {
+                final PolyglotEngine engine = PolyglotEngine.newBuilder().build();
+                TruffleObject fn = (TruffleObject) engine.eval(
+                    Source.newBuilder("eval.bind(this)").
+                        mimeType("text/javascript").
+                        name("eval.js").build()
+                ).get();
+                eval = JavaInterop.asJavaFunction(Eval.class, fn);
+            } catch (RuntimeException ex) {
+                throw ex;
+            } catch (Exception ex) {
+                throw new IllegalStateException(ex);
+            }
+        }
+        return eval;
+    }
+
+    private WrapArray getWrap() {
+        if (copy == null) {
+            TruffleObject fn = (TruffleObject) getEval().eval("(function(arr) {\n"
+                + "  var n = [];\n"
+                + "  for (var i = 0; i < arr.length; i++) {\n"
+                + "    n[i] = arr[i];\n"
+                + "  }\n"
+                + "  return n;\n"
+                + "}).bind(this)"
+            );
+            copy = JavaInterop.asJavaFunction(WrapArray.class, fn);
+        }
+        return copy;
+    }
+
+    private Apply getApply() {
+        if (apply == null) {
+            TruffleObject fn = (TruffleObject) getEval().eval("(function() {\n"
+                + "  var args = Array.prototype.slice.call(arguments);\n"
+                + "  var fn = args.shift();\n"
+                + "  return fn.apply(null, args);\n"
+                + "}).bind(this)"
+            );
+            apply = JavaInterop.asJavaFunction(Apply.class, fn);
+        }
+        return apply;
+    }
+
+    private TruffleObject jsNull() {
+        if (jsNull == null) {
+            jsNull = (TruffleObject) getEval().eval("null"); // NOI18N
+        }
+        return jsNull;
+    }
+
+    private class FnImpl extends Fn {
+
+        private final TruffleObject fn;
+
+        public FnImpl(Presenter presenter, TruffleObject fn, int arity) {
+            super(presenter);
+            this.fn = fn;
+        }
+
+        @Override
+        public Object invoke(Object thiz, Object... args) throws Exception {
+            List<Object> all = new ArrayList<>(args.length + 1);
+            all.add(fn);
+            all.add(thiz == null ? jsNull() : toJavaScript(thiz));
+            for (Object conv : args) {
+                conv = toJavaScript(conv);
+                all.add(conv);
+            }
+            Object ret = getApply().apply(all.toArray());
+            if (ret instanceof JavaValue) {
+                ret = ((JavaValue)ret).get();
+            }
+            if (ret == fn) {
+                return null;
+            }
+            return toJava(ret);
+        }
+    }
+
+    static abstract class JavaLang extends TruffleLanguage<Object> {
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-truffle/src/main/java/net/java/html/boot/truffle/TrufflePresenters.java
----------------------------------------------------------------------
diff --git a/boot-truffle/src/main/java/net/java/html/boot/truffle/TrufflePresenters.java b/boot-truffle/src/main/java/net/java/html/boot/truffle/TrufflePresenters.java
new file mode 100644
index 0000000..47df9d3
--- /dev/null
+++ b/boot-truffle/src/main/java/net/java/html/boot/truffle/TrufflePresenters.java
@@ -0,0 +1,66 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.boot.truffle;
+
+import java.util.concurrent.Executor;
+import org.netbeans.html.boot.spi.Fn;
+
+
+/** Integration with Truffle and
+ * <a href="http://www.oracle.com/technetwork/oracle-labs/program-languages/overview/">GraalVM</a>.
+ *
+ * @since 1.4
+ */
+public final class TrufflePresenters {
+    private TrufflePresenters() {
+    }
+
+    /** Creates new instance of Truffle based presenter.
+     *
+     * @param executor the executor to run requests in
+     * @return new instance of the presenter
+     */
+    public static Fn.Presenter create(Executor executor) {
+        return new TrufflePresenter(executor, null);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-truffle/src/main/java/net/java/html/boot/truffle/package.html
----------------------------------------------------------------------
diff --git a/boot-truffle/src/main/java/net/java/html/boot/truffle/package.html b/boot-truffle/src/main/java/net/java/html/boot/truffle/package.html
new file mode 100644
index 0000000..321c3e9
--- /dev/null
+++ b/boot-truffle/src/main/java/net/java/html/boot/truffle/package.html
@@ -0,0 +1,51 @@
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<body>
+    <p>
+        Integration with <a href="http://github.com/graalvm/truffle">Truffle</a>
+        useful to execute against <b>node.js</b> running on top of
+        the <a href="http://www.oracle.com/technetwork/oracle-labs/program-languages/overview/">GraalVM</a>.
+    </p>
+</body>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-truffle/src/test/java/net/java/html/boot/truffle/JsArrayTruffleTest.java
----------------------------------------------------------------------
diff --git a/boot-truffle/src/test/java/net/java/html/boot/truffle/JsArrayTruffleTest.java b/boot-truffle/src/test/java/net/java/html/boot/truffle/JsArrayTruffleTest.java
new file mode 100644
index 0000000..1b7c4f0
--- /dev/null
+++ b/boot-truffle/src/test/java/net/java/html/boot/truffle/JsArrayTruffleTest.java
@@ -0,0 +1,140 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.boot.truffle;
+
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.vm.PolyglotEngine;
+import java.io.Closeable;
+import java.io.IOException;
+import net.java.html.lib.Array;
+import net.java.html.lib.Function;
+import org.netbeans.html.boot.spi.Fn;
+import org.testng.Assert;
+import static org.testng.Assert.assertEquals;
+import org.testng.SkipException;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class JsArrayTruffleTest {
+    private Fn.Presenter presenter;
+    private Closeable close;
+    
+    @BeforeMethod
+    public void initializePresenter() throws Exception {
+        PolyglotEngine engine = PolyglotEngine.newBuilder().build();
+        int fourtyTwo;
+        try {
+            fourtyTwo = engine.eval(
+                Source.newBuilder("6 * 7").
+                mimeType("text/javascript").
+                name("meaning.js").
+                build()
+            ).as(Number.class).intValue();
+            presenter = TrufflePresenters.create(null);
+            close = Fn.activate(presenter);
+        } catch (Throwable ex) {
+            fourtyTwo = 42;
+        }
+        assertEquals(fourtyTwo, 42, "Meaning of Graal");
+
+    }
+
+    @AfterMethod
+    public void closePresenter() throws IOException {
+        if (close != null) {
+            close.close();
+        }
+    }
+
+
+    @Test
+    public void forEachArray() {
+        if (presenter == null) {
+            throw new SkipException("No presenter found, not running on GraalVM");
+        }
+        Array<String> array = new Array<>();
+        array.push("Hello");
+        array.push("World");
+        array.push("how");
+        array.push("are", "You?");
+
+        Assert.assertEquals(array.length(), 5, "Five words");
+
+        final Array<String> lowerCaseArray = new Array<>();
+        array.forEach(new Function.A1<String, Void>() {
+            @Override
+            public Void call(String p1) {
+                lowerCaseArray.push(p1.toLowerCase());
+                return null;
+            }
+
+            @Override
+            public Void call(String p1, Object p2) {
+                return call(p1);
+            }
+
+            @Override
+            public Void call(String p1, Object p2, Object p3) {
+                return call(p1);
+            }
+
+            @Override
+            public Void call(String p1, Object p2, Object p3, Object p4) {
+                return call(p1);
+            }
+
+            @Override
+            public Void call(String p1, Object p2, Object p3, Object p4, Object p5) {
+                return call(p1);
+            }
+        });
+
+        assertEquals(lowerCaseArray.$get(0), "hello");
+        assertEquals(lowerCaseArray.$get(1), "world");
+        assertEquals(lowerCaseArray.$get(2), "how");
+        assertEquals(lowerCaseArray.$get(3), "are");
+        assertEquals(lowerCaseArray.$get(4), "you?");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-truffle/src/test/java/net/java/html/boot/truffle/SingleCase.java
----------------------------------------------------------------------
diff --git a/boot-truffle/src/test/java/net/java/html/boot/truffle/SingleCase.java b/boot-truffle/src/test/java/net/java/html/boot/truffle/SingleCase.java
new file mode 100644
index 0000000..7bf99b0
--- /dev/null
+++ b/boot-truffle/src/test/java/net/java/html/boot/truffle/SingleCase.java
@@ -0,0 +1,127 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.boot.truffle;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import org.netbeans.html.boot.spi.Fn;
+import org.netbeans.html.boot.impl.FnContext;
+import org.testng.IHookCallBack;
+import org.testng.IHookable;
+import org.testng.ITest;
+import org.testng.ITestResult;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public final class SingleCase implements ITest, IHookable, Runnable {
+    static final Executor JS = Executors.newSingleThreadExecutor();
+    private final Fn.Presenter p;
+    private final Method m;
+    private Object result;
+    private Object inst;
+
+    SingleCase(Fn.Presenter p, Method m) {
+        this.p = p;
+        this.m = m;
+    }
+
+    @Override
+    public String getTestName() {
+        return m.getName();
+    }
+
+    @Test
+    public synchronized void executeTest() throws Exception {
+        if (result == null) {
+            JS.execute(this);
+            wait();
+        }
+        if (result instanceof Exception) {
+            throw (Exception)result;
+        }
+        if (result instanceof Error) {
+            throw (Error)result;
+        }
+    }
+
+    @Override
+    public synchronized void run() {
+        boolean notify = true;
+        try {
+            FnContext.currentPresenter(p);
+            if (inst == null) {
+                inst = m.getDeclaringClass().newInstance();
+            }
+            result = m.invoke(inst);
+            if (result == null) {
+                result = this;
+            }
+        } catch (InvocationTargetException ex) {
+            Throwable r = ex.getTargetException();
+            if (r instanceof InterruptedException) {
+                notify = false;
+                JS.execute(this);
+                return;
+            }
+            result = r;
+        } catch (Exception ex) {
+            result = ex;
+        } finally {
+            if (notify) {
+                notifyAll();
+            }
+            FnContext.currentPresenter(null);
+        }
+    }
+
+    @Override
+    public void run(IHookCallBack ihcb, ITestResult itr) {
+        ihcb.runTestMethod(itr);
+    }
+    
+}



[22/24] incubator-netbeans-html4j git commit: [INFRA-15006] Initial donation of HTML/Java NetBeans API. Equivalent to the content of html4j-donation-review.zip donated as part of ApacheNetBeansDonation1.zip with SHA256 being 7f2ca0f61953a190613c9a0fbcc1b

Posted by jt...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/test/java/org/netbeans/html/boot/fx/FXJavaScriptTest.java
----------------------------------------------------------------------
diff --git a/boot-fx/src/test/java/org/netbeans/html/boot/fx/FXJavaScriptTest.java b/boot-fx/src/test/java/org/netbeans/html/boot/fx/FXJavaScriptTest.java
new file mode 100644
index 0000000..f531d4c
--- /dev/null
+++ b/boot-fx/src/test/java/org/netbeans/html/boot/fx/FXJavaScriptTest.java
@@ -0,0 +1,124 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.fx;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executors;
+import net.java.html.BrwsrCtx;
+import net.java.html.boot.BrowserBuilder;
+import org.netbeans.html.boot.spi.Fn;
+import org.netbeans.html.json.tck.KOTest;
+import org.testng.Assert;
+import static org.testng.Assert.assertNotSame;
+import static org.testng.Assert.assertSame;
+import org.testng.annotations.Factory;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class FXJavaScriptTest {
+    private static Class<?> browserClass;
+    private static Fn.Presenter browserPresenter;
+    
+    public FXJavaScriptTest() {
+    }
+
+    @Factory public static Object[] compatibilityTests() throws Exception {
+        final BrowserBuilder bb = BrowserBuilder.newBrowser().loadClass(FXJavaScriptTest.class).
+            loadPage("empty.html").
+            invoke("initialized");
+
+        Executors.newSingleThreadExecutor().submit(new Runnable() {
+            @Override
+            public void run() {
+                bb.showAndWait();
+            }
+        });
+
+        List<Object> res = new ArrayList<Object>();
+        Class<? extends Annotation> test = 
+            loadClass().getClassLoader().loadClass(KOTest.class.getName()).
+            asSubclass(Annotation.class);
+
+        Class[] arr = (Class[]) loadClass().getDeclaredMethod("tests").invoke(null);
+        for (Class c : arr) {
+            for (Method m : c.getMethods()) {
+                if (m.getAnnotation(test) != null) {
+                    res.add(new KOFx(browserPresenter, m));
+                }
+            }
+        }
+        return res.toArray();
+    }
+
+    static synchronized Class<?> loadClass() throws InterruptedException {
+        while (browserClass == null) {
+            FXJavaScriptTest.class.wait();
+        }
+        return browserClass;
+    }
+    
+    public static synchronized void ready(Class<?> browserCls) throws Exception {
+        browserClass = browserCls;
+        browserPresenter = Fn.activePresenter();
+        FXJavaScriptTest.class.notifyAll();
+    }
+    
+    public static void initialized() throws Exception {
+        BrwsrCtx b1 = BrwsrCtx.findDefault(FXJavaScriptTest.class);
+        TestingProvider.assertCalled("Our context created");
+        assertNotSame(b1, BrwsrCtx.EMPTY, "Browser context is not empty");
+        BrwsrCtx b2 = BrwsrCtx.findDefault(FXJavaScriptTest.class);
+        assertSame(b1, b2, "Browser context remains stable");
+        Assert.assertSame(
+            FXJavaScriptTest.class.getClassLoader(),
+            ClassLoader.getSystemClassLoader(),
+            "No special classloaders"
+        );
+        FXJavaScriptTest.ready(FxJavaScriptTst.class);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/test/java/org/netbeans/html/boot/fx/FXPresenterTst.java
----------------------------------------------------------------------
diff --git a/boot-fx/src/test/java/org/netbeans/html/boot/fx/FXPresenterTst.java b/boot-fx/src/test/java/org/netbeans/html/boot/fx/FXPresenterTst.java
new file mode 100644
index 0000000..8a93ad3
--- /dev/null
+++ b/boot-fx/src/test/java/org/netbeans/html/boot/fx/FXPresenterTst.java
@@ -0,0 +1,103 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.fx;
+
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import net.java.html.js.JavaScriptBody;
+import static org.testng.Assert.*;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class FXPresenterTst {
+    @Test public void showClassLoader() {
+        R run = new R();
+        callback(run);
+        assertEquals(run.cnt, 1, "Can call even private implementation classes");
+    }
+    
+    @Test public void checkConsoleLogging() {
+        class H extends Handler {
+            LogRecord record;
+            
+            @Override
+            public void publish(LogRecord record) {
+                assert this.record == null;
+                this.record = record;
+            }
+
+            @Override
+            public void flush() {
+            }
+
+            @Override
+            public void close() throws SecurityException {
+            }
+        }
+        H h = new H();
+        FXConsole.LOG.addHandler(h);
+
+        log("Ahoj");
+        
+        assert h.record != null : "Some log record obtained";
+        assert "Ahoj".equals(h.record.getMessage()) : "It is our Ahoj: " + h.record.getMessage();
+    }
+    
+    @JavaScriptBody(args = { "r" }, javacall = true, body = "r.@java.lang.Runnable::run()();")
+    private static native void callback(Runnable r);
+
+    @JavaScriptBody(args = { "msg" }, body = "console.log(msg);")
+    private static native void log(String msg);
+
+    private static class R implements Runnable {
+        int cnt;
+
+        @Override
+        public void run() {
+            cnt++;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/test/java/org/netbeans/html/boot/fx/FxJavaScriptTst.java
----------------------------------------------------------------------
diff --git a/boot-fx/src/test/java/org/netbeans/html/boot/fx/FxJavaScriptTst.java b/boot-fx/src/test/java/org/netbeans/html/boot/fx/FxJavaScriptTst.java
new file mode 100644
index 0000000..5a1a042
--- /dev/null
+++ b/boot-fx/src/test/java/org/netbeans/html/boot/fx/FxJavaScriptTst.java
@@ -0,0 +1,55 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.fx;
+
+import org.netbeans.html.json.tck.JavaScriptTCK;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public final class FxJavaScriptTst extends JavaScriptTCK {
+    public static Class[] tests() {
+        return testClasses();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/test/java/org/netbeans/html/boot/fx/KOFx.java
----------------------------------------------------------------------
diff --git a/boot-fx/src/test/java/org/netbeans/html/boot/fx/KOFx.java b/boot-fx/src/test/java/org/netbeans/html/boot/fx/KOFx.java
new file mode 100644
index 0000000..6d12324
--- /dev/null
+++ b/boot-fx/src/test/java/org/netbeans/html/boot/fx/KOFx.java
@@ -0,0 +1,125 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.fx;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import javafx.application.Platform;
+import org.netbeans.html.boot.impl.FnContext;
+import org.netbeans.html.boot.spi.Fn;
+import org.testng.IHookCallBack;
+import org.testng.IHookable;
+import org.testng.ITest;
+import org.testng.ITestResult;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public final class KOFx implements ITest, IHookable, Runnable {
+    private final Fn.Presenter p;
+    private final Method m;
+    private Object result;
+    private Object inst;
+
+    KOFx(Fn.Presenter p, Method m) {
+        this.p = p;
+        this.m = m;
+    }
+
+    @Override
+    public String getTestName() {
+        return m.getName();
+    }
+
+    @Test
+    public synchronized void executeTest() throws Exception {
+        if (result == null) {
+            Platform.runLater(this);
+            wait();
+        }
+        if (result instanceof Exception) {
+            throw (Exception)result;
+        }
+        if (result instanceof Error) {
+            throw (Error)result;
+        }
+    }
+
+    @Override
+    public synchronized void run() {
+        boolean notify = true;
+        try {
+            FnContext.currentPresenter(p);
+            if (inst == null) {
+                inst = m.getDeclaringClass().newInstance();
+            }
+            result = m.invoke(inst);
+            if (result == null) {
+                result = this;
+            }
+        } catch (InvocationTargetException ex) {
+            Throwable r = ex.getTargetException();
+            if (r instanceof InterruptedException) {
+                notify = false;
+                Platform.runLater(this);
+                return;
+            }
+            result = r;
+        } catch (Exception ex) {
+            result = ex;
+        } finally {
+            if (notify) {
+                notifyAll();
+            }
+            FnContext.currentPresenter(null);
+        }
+    }
+
+    @Override
+    public void run(IHookCallBack ihcb, ITestResult itr) {
+        ihcb.runTestMethod(itr);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/test/java/org/netbeans/html/boot/fx/ReloadTest.java
----------------------------------------------------------------------
diff --git a/boot-fx/src/test/java/org/netbeans/html/boot/fx/ReloadTest.java b/boot-fx/src/test/java/org/netbeans/html/boot/fx/ReloadTest.java
new file mode 100644
index 0000000..5a704f5
--- /dev/null
+++ b/boot-fx/src/test/java/org/netbeans/html/boot/fx/ReloadTest.java
@@ -0,0 +1,157 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.fx;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import javafx.application.Platform;
+import net.java.html.BrwsrCtx;
+import net.java.html.boot.BrowserBuilder;
+import net.java.html.js.JavaScriptBody;
+import org.netbeans.html.boot.spi.Fn;
+import static org.testng.Assert.*;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class ReloadTest {
+    private static Runnable whenInitialized;
+    
+    public ReloadTest() {
+    }
+
+    @JavaScriptBody(args = { "a", "b"  }, body = "return a + b;")
+    private static native int plus(int a, int b);
+    
+    @Test public void checkReload() throws Throwable {
+        final Throwable[] arr = { null };
+        
+        final BrowserBuilder bb = BrowserBuilder.newBrowser().loadClass(ReloadTest.class).
+                loadPage("empty.html").
+                invoke("initialized");
+        
+        class ShowBrowser implements Runnable {
+            @Override
+            public void run() {
+                bb.showAndWait();
+            }
+        }
+        
+        class WhenInitialized implements Runnable {
+            CountDownLatch cdl = new CountDownLatch(1);
+            AbstractFXPresenter p;
+            BrwsrCtx ctx;
+            
+            @Override
+            public void run() {
+                try {
+                    whenInitialized = null;
+                    doCheckReload();
+                    p = (AbstractFXPresenter) Fn.activePresenter();
+                    assertNotNull(p, "Presenter is defined");
+                    ctx = BrwsrCtx.findDefault(WhenInitialized.class);
+                } catch (Throwable ex) {
+                    arr[0] = ex;
+                } finally {
+                    cdl.countDown();
+                }
+            }
+        }
+        WhenInitialized when = new WhenInitialized();
+        whenInitialized = when;
+        Executors.newSingleThreadExecutor().submit(new ShowBrowser());
+        when.cdl.await();
+        if (arr[0] != null) throw arr[0];
+        
+        class ReloadPage implements Runnable {
+            final CountDownLatch cdl = new CountDownLatch(1);
+            private final AbstractFXPresenter p;
+
+            public ReloadPage(AbstractFXPresenter p) {
+                this.p = p;
+            }
+
+            @Override
+            public void run() {
+                p.engine.reload();
+                cdl.countDown();
+            }
+        }
+        ReloadPage relPage = new ReloadPage(when.p);
+        
+        class SecondInit implements Runnable {
+            CountDownLatch cdl = new CountDownLatch(1);
+            
+            @Override
+            public void run() {
+                try {
+                    whenInitialized = null;
+                    doCheckReload();
+                } catch (Throwable ex) {
+                    arr[0] = ex;
+                } finally {
+                    cdl.countDown();
+                }
+                
+            }
+        }
+        SecondInit second = new SecondInit();
+        whenInitialized = second;
+        
+        Platform.runLater(relPage);
+        
+        second.cdl.await();
+        if (arr[0] != null) throw arr[0];
+    }
+    
+    final void doCheckReload() throws Exception {
+        int res = plus(30, 12);
+        assertEquals(res, 42, "Meaning of world computed");
+    }
+    
+    public static synchronized void initialized() throws Exception {
+        whenInitialized.run();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/test/java/org/netbeans/html/boot/fx/TestingProvider.java
----------------------------------------------------------------------
diff --git a/boot-fx/src/test/java/org/netbeans/html/boot/fx/TestingProvider.java b/boot-fx/src/test/java/org/netbeans/html/boot/fx/TestingProvider.java
new file mode 100644
index 0000000..a79baef
--- /dev/null
+++ b/boot-fx/src/test/java/org/netbeans/html/boot/fx/TestingProvider.java
@@ -0,0 +1,65 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.fx;
+
+import org.netbeans.html.context.spi.Contexts;
+import org.openide.util.lookup.ServiceProvider;
+import static org.testng.Assert.assertTrue;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+@ServiceProvider(service = Contexts.Provider.class)
+public final class TestingProvider implements Contexts.Provider {
+    
+    static void assertCalled(String msg) {
+        assertTrue(Boolean.getBoolean(TestingProvider.class.getName()), msg);
+    }
+
+    @Override
+    public void fillContext(Contexts.Builder context, Class<?> requestor) {
+        System.setProperty(TestingProvider.class.getName(), "true");
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/test/java/org/netbeans/html/boot/fx/TitleTest.java
----------------------------------------------------------------------
diff --git a/boot-fx/src/test/java/org/netbeans/html/boot/fx/TitleTest.java b/boot-fx/src/test/java/org/netbeans/html/boot/fx/TitleTest.java
new file mode 100644
index 0000000..8b09be3
--- /dev/null
+++ b/boot-fx/src/test/java/org/netbeans/html/boot/fx/TitleTest.java
@@ -0,0 +1,146 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.fx;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.stage.Stage;
+import net.java.html.BrwsrCtx;
+import net.java.html.boot.BrowserBuilder;
+import net.java.html.js.JavaScriptBody;
+import org.netbeans.html.boot.spi.Fn;
+import static org.testng.Assert.*;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class TitleTest {
+    private static Runnable whenInitialized;
+    
+    public TitleTest() {
+    }
+
+    @JavaScriptBody(args = { "a", "b"  }, body = "return a + b;")
+    private static native int plus(int a, int b);
+    
+    @Test public void checkReload() throws Throwable {
+        final Throwable[] arr = { null };
+        
+        final BrowserBuilder bb = BrowserBuilder.newBrowser().loadClass(TitleTest.class).
+                loadPage("empty.html").
+                invoke("initialized");
+        
+        class ShowBrowser implements Runnable {
+            @Override
+            public void run() {
+                bb.showAndWait();
+            }
+        }
+        
+        class WhenInitialized implements Runnable {
+            CountDownLatch cdl = new CountDownLatch(1);
+            AbstractFXPresenter p;
+            BrwsrCtx ctx;
+            
+            @Override
+            public void run() {
+                try {
+                    whenInitialized = null;
+                    doCheckReload();
+                    p = (AbstractFXPresenter) Fn.activePresenter();
+                    assertNotNull(p, "Presenter is defined");
+                    ctx = BrwsrCtx.findDefault(WhenInitialized.class);
+                } catch (Throwable ex) {
+                    arr[0] = ex;
+                } finally {
+                    cdl.countDown();
+                }
+            }
+        }
+        WhenInitialized when = new WhenInitialized();
+        whenInitialized = when;
+        Executors.newSingleThreadExecutor().submit(new ShowBrowser());
+        when.cdl.await();
+        if (arr[0] != null) throw arr[0];
+        
+        Stage s = FXBrwsr.findStage();
+        assertEquals(s.getTitle(), "FX Presenter Harness");
+        
+        final CountDownLatch propChange = new CountDownLatch(1);
+        s.titleProperty().addListener(new ChangeListener<String>() {
+            @Override
+            public void changed(ObservableValue<? extends String> ov, String t, String t1) {
+                propChange.countDown();
+            }
+        });
+        
+        when.ctx.execute(new Runnable() {
+            @Override
+            public void run() {
+                changeTitle("New title");
+            }
+        });
+
+        propChange.await(5, TimeUnit.SECONDS);
+        assertEquals(s.getTitle(), "New title");
+    }
+    
+    final void doCheckReload() throws Exception {
+        int res = plus(30, 12);
+        assertEquals(res, 42, "Meaning of world computed");
+    }
+    
+    public static synchronized void initialized() throws Exception {
+        whenInitialized.run();
+    }
+    
+    @JavaScriptBody(args = { "s" }, body = 
+        "document.getElementsByTagName('title')[0].innerHTML = s;"
+    )
+    static native void changeTitle(String s);
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/test/java/org/sample/app/pkg/SampleApp.java
----------------------------------------------------------------------
diff --git a/boot-fx/src/test/java/org/sample/app/pkg/SampleApp.java b/boot-fx/src/test/java/org/sample/app/pkg/SampleApp.java
new file mode 100644
index 0000000..ab83113
--- /dev/null
+++ b/boot-fx/src/test/java/org/sample/app/pkg/SampleApp.java
@@ -0,0 +1,62 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.sample.app.pkg;
+
+import net.java.html.boot.BrowserBuilder;
+import org.netbeans.html.boot.fx.FXBrwsrTest;
+
+public class SampleApp implements Runnable {
+
+    public static void main() {
+        BrowserBuilder.newBrowser()
+            .loadPage("org/netbeans/html/boot/fx/empty.html") // NOI18N
+            .loadFinished(new SampleApp())
+            .showAndWait();
+    }
+
+    @Override
+    public void run() {
+        FXBrwsrTest.computeCalleeClassName();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/test/resources/net/java/html/boot/fx/wnd.js
----------------------------------------------------------------------
diff --git a/boot-fx/src/test/resources/net/java/html/boot/fx/wnd.js b/boot-fx/src/test/resources/net/java/html/boot/fx/wnd.js
new file mode 100644
index 0000000..acd0f9e
--- /dev/null
+++ b/boot-fx/src/test/resources/net/java/html/boot/fx/wnd.js
@@ -0,0 +1,49 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+if (typeof wnd !== 'undefined') {
+    throw 'Window should not be defined yet: ' + wnd;
+}
+
+wnd = {
+};
+

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/test/resources/org/netbeans/html/boot/fx/empty.html
----------------------------------------------------------------------
diff --git a/boot-fx/src/test/resources/org/netbeans/html/boot/fx/empty.html b/boot-fx/src/test/resources/org/netbeans/html/boot/fx/empty.html
new file mode 100644
index 0000000..c292ab3
--- /dev/null
+++ b/boot-fx/src/test/resources/org/netbeans/html/boot/fx/empty.html
@@ -0,0 +1,55 @@
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>FX Presenter Harness</title>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <meta name="viewport" content="width=device-width">
+    </head>
+    <body>
+        <div>FX Presenter Harness</div>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-script/pom.xml
----------------------------------------------------------------------
diff --git a/boot-script/pom.xml b/boot-script/pom.xml
new file mode 100644
index 0000000..55073fd
--- /dev/null
+++ b/boot-script/pom.xml
@@ -0,0 +1,152 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2016 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<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">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.netbeans.html</groupId>
+        <artifactId>pom</artifactId>
+        <version>2.0-SNAPSHOT</version>
+    </parent>
+    <name>Presenter via javax.script</name>
+    <artifactId>net.java.html.boot.script</artifactId>
+    <version>2.0-SNAPSHOT</version>
+    <packaging>bundle</packaging>
+    <properties>
+        <netbeans.compile.on.save>NONE</netbeans.compile.on.save>
+        <publicPackages>net.java.html.boot.script</publicPackages>
+    </properties>
+    <build>
+      <plugins>
+          <plugin>
+              <groupId>org.apache.felix</groupId>
+              <artifactId>maven-bundle-plugin</artifactId>
+              <extensions>true</extensions>
+              <configuration>
+                  <instructions>
+                      <Export-Package>${publicPackages}</Export-Package>
+                      <Bundle-SymbolicName>net.java.html.boot.script</Bundle-SymbolicName>
+                  </instructions>
+              </configuration>
+          </plugin>
+          <plugin>
+              <groupId>org.netbeans.html</groupId>
+              <artifactId>html4j-maven-plugin</artifactId>
+          </plugin>
+         <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-compiler-plugin</artifactId>
+            <version>2.3.2</version>
+            <configuration>
+               <source>1.7</source>
+               <target>1.7</target>
+            </configuration>
+         </plugin>
+      </plugins>
+    </build>
+    <dependencies>
+        <dependency>
+            <groupId>org.netbeans.api</groupId>
+            <artifactId>org-openide-util-lookup</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.netbeans.html</groupId>
+            <artifactId>net.java.html.boot</artifactId>
+            <version>${project.version}</version>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>org.testng</groupId>
+            <artifactId>testng</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>net.java.html.json.tck</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.grizzly</groupId>
+            <artifactId>grizzly-http-server</artifactId>
+            <version>${grizzly.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.grizzly</groupId>
+            <artifactId>grizzly-websockets-server</artifactId>
+            <version>${grizzly.version}</version>
+            <scope>test</scope>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.grizzly</groupId>
+            <artifactId>grizzly-http-servlet</artifactId>
+            <version>${grizzly.version}</version>
+            <scope>test</scope>
+        </dependency>    
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <scope>test</scope>
+            <version>3.1.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.netbeans.html</groupId>
+            <artifactId>ko4j</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ko-ws-tyrus</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+            <type>jar</type>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-script/src/main/java/net/java/html/boot/script/ScriptPresenter.java
----------------------------------------------------------------------
diff --git a/boot-script/src/main/java/net/java/html/boot/script/ScriptPresenter.java b/boot-script/src/main/java/net/java/html/boot/script/ScriptPresenter.java
new file mode 100644
index 0000000..82dabff
--- /dev/null
+++ b/boot-script/src/main/java/net/java/html/boot/script/ScriptPresenter.java
@@ -0,0 +1,443 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.boot.script;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.ObjectOutput;
+import java.io.Reader;
+import java.lang.ref.WeakReference;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.script.Invocable;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+import org.netbeans.html.boot.spi.Fn;
+import org.netbeans.html.boot.spi.Fn.Presenter;
+
+/** Implementation of {@link Presenter} that delegates
+ * to Java {@link ScriptEngine scripting} API. The presenter runs headless
+ * without appropriate simulation of browser APIs. Its primary usefulness
+ * is inside testing environments. 
+ * <p>
+ * One can load in browser simulation for example from 
+ * <a href="http://www.envjs.com/">env.js</a>. The best way to achieve so,
+ * is to wait until JDK-8046013 gets fixed....
+ * 
+ *
+ * @author Jaroslav Tulach
+ */
+final class ScriptPresenter implements Fn.KeepAlive,
+Presenter, Fn.FromJavaScript, Fn.ToJavaScript, Executor {
+    private static final Logger LOG = Logger.getLogger(ScriptPresenter.class.getName());
+    private static final boolean JDK7;
+    static {
+        boolean jdk7;
+        try {
+            Class.forName("java.lang.FunctionalInterface");
+            jdk7 = false;
+        } catch (ClassNotFoundException ex) {
+            jdk7 = true;
+        }
+        JDK7 = jdk7;
+    }
+    private final ScriptEngine eng;
+    private final Executor exc;
+    private final Object undefined;
+
+    public ScriptPresenter(Executor exc) {
+        this.exc = exc;
+        try {
+            eng = new ScriptEngineManager().getEngineByName("javascript");
+            eng.eval("function alert(msg) { Packages.java.lang.System.out.println(msg); };");
+            eng.eval("function confirm(msg) { Packages.java.lang.System.out.println(msg); return true; };");
+            eng.eval("function prompt(msg, txt) { Packages.java.lang.System.out.println(msg + ':' + txt); return txt; };");
+            Object undef;
+            if (JDK7) {
+                undef = new JDK7Callback().undefined(eng);
+            } else {
+                undef = ((Object[])eng.eval("Java.to([undefined])"))[0];
+            }
+            this.undefined = undef;
+        } catch (ScriptException ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+
+    @Override
+    public Fn defineFn(String code, String... names) {
+        return defineImpl(code, names, null);
+    }
+
+    @Override
+    public Fn defineFn(String code, String[] names, boolean[] keepAlive) {
+        return defineImpl(code, names, keepAlive);
+    }    
+    private FnImpl defineImpl(String code, String[] names, boolean[] keepAlive) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("(function() {\n");
+        sb.append("  return function(");
+        String sep = "";
+        if (names != null) for (String n : names) {
+            sb.append(sep).append(n);
+            sep = ",";
+        }
+        sb.append(") {\n");
+        sb.append(code);
+        sb.append("\n  };\n");
+        sb.append("})()\n");
+
+        final Object fn;
+        try {
+            fn = eng.eval(sb.toString());
+        } catch (ScriptException ex) {
+            throw new IllegalStateException(ex);
+        }
+        return new FnImpl(this, fn, keepAlive);
+    }
+
+    @Override
+    public void displayPage(URL page, Runnable onPageLoad) {
+        try {
+            eng.eval("if (typeof window !== 'undefined') window.location = '" + page + "'");
+        } catch (ScriptException ex) {
+            LOG.log(Level.SEVERE, "Cannot load " + page, ex);
+        }
+        if (onPageLoad != null) {
+            onPageLoad.run();
+        }
+    }
+
+    @Override
+    public void loadScript(Reader code) throws Exception {
+        eng.eval(code);
+    }
+    
+    //
+    // array conversions
+    //
+    
+    final Object convertArrays(Object[] arr) throws Exception {
+        for (int i = 0; i < arr.length; i++) {
+            if (arr[i] instanceof Object[]) {
+                arr[i] = convertArrays((Object[]) arr[i]);
+            }
+        }
+        final Object wrapArr = wrapArrFn().invokeImpl(null, false, arr); // NOI18N
+        return wrapArr;
+    }
+
+    private FnImpl wrapArrImpl;
+    private FnImpl wrapArrFn() {
+        if (wrapArrImpl == null) {
+            try {
+                wrapArrImpl = defineImpl("return Array.prototype.slice.call(arguments);", null, null);
+            } catch (Exception ex) {
+                throw new IllegalStateException(ex);
+            }
+        }
+        return wrapArrImpl;
+    }
+
+    final Object checkArray(Object val) throws Exception {
+        final FnImpl fn = arraySizeFn();
+        final Object fnRes = fn.invokeImpl(null, false, val, null);
+        int length = ((Number) fnRes).intValue();
+        if (length == -1) {
+            return val;
+        }
+        Object[] arr = new Object[length];
+        fn.invokeImpl(null, false, val, arr);
+        return arr;
+    }
+
+    private FnImpl arraySize;
+    private FnImpl arraySizeFn() {
+        if (arraySize == null) {
+            try {
+                arraySize = defineImpl("\n"
+                    + "if (to === null) {\n"
+                    + "  if (Object.prototype.toString.call(arr) === '[object Array]') return arr.length;\n"
+                    + "  else return -1;\n"
+                    + "} else {\n"
+                    + "  var l = arr.length;\n"
+                    + "  for (var i = 0; i < l; i++) {\n"
+                    + "    to[i] = arr[i] === undefined ? null : arr[i];\n"
+                    + "  }\n"
+                    + "  return l;\n"
+                    + "}", new String[] { "arr", "to" }, null
+                );
+            } catch (Exception ex) {
+                throw new IllegalStateException(ex);
+            }
+        }
+        return arraySize;
+    }
+
+    @Override
+    public Object toJava(Object toJS) {
+        if (toJS instanceof Weak) {
+            toJS = ((Weak)toJS).get();
+        }
+        if (toJS == undefined) {
+            return null;
+        }
+        try {
+            return checkArray(toJS);
+        } catch (Exception ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+    
+    @Override
+    public Object toJavaScript(Object toReturn) {
+        if (toReturn instanceof Object[]) {
+            try {
+                return convertArrays((Object[])toReturn);
+            } catch (Exception ex) {
+                throw new IllegalStateException(ex);
+            }
+        } else {
+            if (JDK7) {
+                if (toReturn instanceof Boolean) {
+                    return ((Boolean)toReturn) ? true : null;
+                }
+            }
+            return toReturn;
+        }
+    }
+
+    @Override
+    public void execute(final Runnable command) {
+        if (Fn.activePresenter() == this) {
+            command.run();
+            return;
+        }
+        
+        class Wrap implements Runnable {
+            public void run() {
+                try (Closeable c = Fn.activate(ScriptPresenter.this)) {
+                    command.run();
+                } catch (IOException ex) {
+                    throw new IllegalStateException(ex);
+                }
+            }
+        }
+        final Runnable wrap = new Wrap();
+        if (exc == null) {
+            wrap.run();
+        } else {
+            exc.execute(wrap);
+        }
+    }
+
+    private class FnImpl extends Fn {
+
+        private final Object fn;
+        private final boolean[] keepAlive;
+
+        public FnImpl(Presenter presenter, Object fn, boolean[] keepAlive) {
+            super(presenter);
+            this.fn = fn;
+            this.keepAlive = keepAlive;
+        }
+
+        @Override
+        public Object invoke(Object thiz, Object... args) throws Exception {
+            return invokeImpl(thiz, true, args);
+        }
+
+            final Object invokeImpl(Object thiz, boolean arrayChecks, Object... args) throws Exception {
+                List<Object> all = new ArrayList<>(args.length + 1);
+                all.add(thiz == null ? fn : thiz);
+                for (int i = 0; i < args.length; i++) {
+                    Object conv = args[i];
+                    if (arrayChecks) {
+                        if (args[i] instanceof Object[]) {
+                            Object[] arr = (Object[]) args[i];
+                            conv = ((ScriptPresenter) presenter()).convertArrays(arr);
+                        }
+                        if (conv != null && keepAlive != null
+                            && !keepAlive[i] && !isJSReady(conv)
+                            && !conv.getClass().getSimpleName().equals("$JsCallbacks$") // NOI18N
+                            ) {
+                            conv = new Weak(conv);
+                        }
+                        if (conv instanceof Character) {
+                            conv = (int)(Character)conv;
+                        }
+                    }
+                    all.add(conv);
+                }
+                Object ret = ((Invocable)eng).invokeMethod(fn, "call", all.toArray()); // NOI18N
+                if (ret instanceof Weak) {
+                    ret = ((Weak)ret).get();
+                }
+                if (ret == fn) {
+                    return null;
+                }
+                if (!arrayChecks) {
+                    return ret;
+                }
+                return ((ScriptPresenter)presenter()).checkArray(ret);
+            }
+    }
+    
+    private static boolean isJSReady(Object obj) {
+        if (obj == null) {
+            return true;
+        }
+        if (obj instanceof String) {
+            return true;
+        }
+        if (obj instanceof Number) {
+            return true;
+        }
+        final String cn = obj.getClass().getName();
+        if (cn.startsWith("jdk.nashorn") || ( // NOI18N
+            cn.contains(".mozilla.") && cn.contains(".Native") // NOI18N
+        )) {
+            return true;
+        }
+        if (obj instanceof Character) {
+            return true;
+        }
+        return false;
+    }    
+    
+    private static final class Weak extends WeakReference<Object> {
+        public Weak(Object referent) {
+            super(referent);
+        }
+    }
+
+    private static final class JDK7Callback implements ObjectOutput {
+        private Object undefined;
+
+        @Override
+        public void writeObject(Object obj) throws IOException {
+            undefined = obj;
+        }
+
+        @Override
+        public void write(int b) throws IOException {
+        }
+
+        @Override
+        public void write(byte[] b) throws IOException {
+        }
+
+        @Override
+        public void write(byte[] b, int off, int len) throws IOException {
+        }
+
+        @Override
+        public void flush() throws IOException {
+        }
+
+        @Override
+        public void close() throws IOException {
+        }
+
+        @Override
+        public void writeBoolean(boolean v) throws IOException {
+        }
+
+        @Override
+        public void writeByte(int v) throws IOException {
+        }
+
+        @Override
+        public void writeShort(int v) throws IOException {
+        }
+
+        @Override
+        public void writeChar(int v) throws IOException {
+        }
+
+        @Override
+        public void writeInt(int v) throws IOException {
+        }
+
+        @Override
+        public void writeLong(long v) throws IOException {
+        }
+
+        @Override
+        public void writeFloat(float v) throws IOException {
+        }
+
+        @Override
+        public void writeDouble(double v) throws IOException {
+        }
+
+        @Override
+        public void writeBytes(String s) throws IOException {
+        }
+
+        @Override
+        public void writeChars(String s) throws IOException {
+        }
+
+        @Override
+        public void writeUTF(String s) throws IOException {
+        }
+
+        public Object undefined(ScriptEngine eng) {
+            try {
+                eng.eval("function isJDK7Undefined(js) { js.writeObject(undefined); }");
+                Invocable inv = (Invocable) eng;
+                inv.invokeFunction("isJDK7Undefined", this);
+            } catch (NoSuchMethodException | ScriptException ex) {
+                throw new IllegalStateException(ex);
+            }
+            return undefined;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-script/src/main/java/net/java/html/boot/script/Scripts.java
----------------------------------------------------------------------
diff --git a/boot-script/src/main/java/net/java/html/boot/script/Scripts.java b/boot-script/src/main/java/net/java/html/boot/script/Scripts.java
new file mode 100644
index 0000000..d5ff411
--- /dev/null
+++ b/boot-script/src/main/java/net/java/html/boot/script/Scripts.java
@@ -0,0 +1,119 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.boot.script;
+
+import java.io.Closeable;
+import java.util.concurrent.Executor;
+import javax.script.ScriptEngine;
+import net.java.html.boot.BrowserBuilder;
+import net.java.html.js.JavaScriptBody;
+import org.netbeans.html.boot.spi.Fn;
+import org.netbeans.html.boot.spi.Fn.Presenter;
+
+/** Implementations of {@link Presenter}s that delegate
+ * to Java {@link ScriptEngine scripting} API. Initialize your presenter
+ * like this:
+ * 
+ * <pre>
+ * 
+ * {@link Runnable} <em>run</em> = ...; // your own init code
+ * {@link Presenter Fn.Presenter} <b>p</b> = Scripts.{@link Scripts#createPresenter()};
+ * BrowserBuilder.{@link BrowserBuilder#newBrowser(java.lang.Object...) newBrowser(<b>p</b>)}.
+ *      {@link BrowserBuilder#loadFinished(java.lang.Runnable) loadFinished(run)}.
+ *      {@link BrowserBuilder#showAndWait()};
+ * </pre>
+ * 
+ * and your runnable can make extensive use of {@link JavaScriptBody} directly or
+ * indirectly via APIs using {@link JavaScriptBody such annotation} themselves.
+ * <p>
+ * Alternatively one can manipulate the presenter manually, which is
+ * especially useful when writing tests:
+ * <pre>
+ * {@code @Test} public void runInASimulatedBrowser() throws Exception {
+ *   {@link Presenter Fn.Presenter} <b>p</b> = Scripts.{@link Scripts#createPresenter()};
+ *   try ({@link Closeable} c = {@link Fn#activate(org.netbeans.html.boot.spi.Fn.Presenter) Fn.activate}(<b>p</b>)) {
+ *     // your code operating in context of <b>p</b>
+ *   }
+ * }
+ * </pre>
+ * The previous code snippet requires Java 7 language syntax, as it relies
+ * on try-with-resources language syntactic sugar feature. The same block
+ * of code can be used on older versions of Java, but it is slightly more
+ * verbose.
+ * 
+ * @author Jaroslav Tulach
+ */
+public final class Scripts {
+    private Scripts() {
+    }
+    
+    /** Simple implementation of {@link Presenter} that delegates
+     * to Java {@link ScriptEngine scripting} API. The presenter runs headless
+     * without appropriate simulation of browser APIs. Its primary usefulness
+     * is inside testing environments. The presenter implements {@link Executor}
+     * interface, but invokes all runnables passed to {@link Executor#execute(java.lang.Runnable)}
+     * immediately.
+     * 
+     * @return new instance of a presenter that is using its own
+     *   {@link ScriptEngine} for <code>text/javascript</code> mimetype
+     */
+    public static Presenter createPresenter() {
+        return new ScriptPresenter(null);
+    }
+
+    /** Implementation of {@link Presenter} that delegates
+     * to Java {@link ScriptEngine scripting} API and can control execution
+     * thread. The presenter runs headless
+     * without appropriate simulation of browser APIs. Its primary usefulness
+     * is inside testing environments. The presenter implements {@link Executor}
+     * interface, and passes all runnables from {@link Executor#execute(java.lang.Runnable)}
+     * to here in provided <code>exc</code> instance.
+     * 
+     * @param exc the executor to re-schedule all asynchronous requests to
+     * @return new instance of a presenter that is using its own
+     *   {@link ScriptEngine} for <code>text/javascript</code> mimetype
+     */
+    public static Presenter createPresenter(Executor exc) {
+        return new ScriptPresenter(exc);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-script/src/main/java/net/java/html/boot/script/package.html
----------------------------------------------------------------------
diff --git a/boot-script/src/main/java/net/java/html/boot/script/package.html b/boot-script/src/main/java/net/java/html/boot/script/package.html
new file mode 100644
index 0000000..f700a76
--- /dev/null
+++ b/boot-script/src/main/java/net/java/html/boot/script/package.html
@@ -0,0 +1,51 @@
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<body>
+    <p>
+        {@link net.java.html.boot.script.Scripts Factories} to create headless
+        {@link net.java.html.boot.BrowserBuilder browser environment} which is
+        useful for testing.
+    </p>
+</body>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-script/src/test/java/net/java/html/boot/script/Jsr223JavaScriptTest.java
----------------------------------------------------------------------
diff --git a/boot-script/src/test/java/net/java/html/boot/script/Jsr223JavaScriptTest.java b/boot-script/src/test/java/net/java/html/boot/script/Jsr223JavaScriptTest.java
new file mode 100644
index 0000000..8868d62
--- /dev/null
+++ b/boot-script/src/test/java/net/java/html/boot/script/Jsr223JavaScriptTest.java
@@ -0,0 +1,117 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.boot.script;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executors;
+import net.java.html.boot.BrowserBuilder;
+import org.netbeans.html.boot.spi.Fn;
+import org.netbeans.html.json.tck.KOTest;
+import org.testng.Assert;
+import org.testng.annotations.Factory;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class Jsr223JavaScriptTest {
+    private static Class<?> browserClass;
+    private static Fn.Presenter browserPresenter;
+    
+    public Jsr223JavaScriptTest() {
+    }
+
+    @Factory public static Object[] compatibilityTests() throws Exception {
+        final BrowserBuilder bb = BrowserBuilder.newBrowser(new ScriptPresenter(SingleCase.JS)).
+            loadClass(Jsr223JavaScriptTest.class).
+            loadPage("empty.html").
+            invoke("initialized");
+
+        Executors.newSingleThreadExecutor().submit(new Runnable() {
+            @Override
+            public void run() {
+                bb.showAndWait();
+            }
+        });
+
+        List<Object> res = new ArrayList<Object>();
+        Class<? extends Annotation> test = 
+            loadClass().getClassLoader().loadClass(KOTest.class.getName()).
+            asSubclass(Annotation.class);
+
+        Class[] arr = (Class[]) loadClass().getDeclaredMethod("tests").invoke(null);
+        for (Class c : arr) {
+            for (Method m : c.getMethods()) {
+                if (m.getAnnotation(test) != null) {
+                    res.add(new SingleCase(browserPresenter, m));
+                }
+            }
+        }
+        return res.toArray();
+    }
+
+    static synchronized Class<?> loadClass() throws InterruptedException {
+        while (browserClass == null) {
+            Jsr223JavaScriptTest.class.wait();
+        }
+        return browserClass;
+    }
+    
+    public static synchronized void ready(Class<?> browserCls) throws Exception {
+        browserClass = browserCls;
+        browserPresenter = Fn.activePresenter();
+        Jsr223JavaScriptTest.class.notifyAll();
+    }
+    
+    public static void initialized() throws Exception {
+        Assert.assertSame(
+            Jsr223JavaScriptTest.class.getClassLoader(),
+            ClassLoader.getSystemClassLoader(),
+            "No special classloaders"
+        );
+        Jsr223JavaScriptTest.ready(Jsr223JavaScriptTst.class);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-script/src/test/java/net/java/html/boot/script/Jsr223JavaScriptTst.java
----------------------------------------------------------------------
diff --git a/boot-script/src/test/java/net/java/html/boot/script/Jsr223JavaScriptTst.java b/boot-script/src/test/java/net/java/html/boot/script/Jsr223JavaScriptTst.java
new file mode 100644
index 0000000..56fde22
--- /dev/null
+++ b/boot-script/src/test/java/net/java/html/boot/script/Jsr223JavaScriptTst.java
@@ -0,0 +1,55 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.boot.script;
+
+import org.netbeans.html.json.tck.JavaScriptTCK;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public final class Jsr223JavaScriptTst extends JavaScriptTCK {
+    public static Class[] tests() {
+        return testClasses();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-script/src/test/java/net/java/html/boot/script/SingleCase.java
----------------------------------------------------------------------
diff --git a/boot-script/src/test/java/net/java/html/boot/script/SingleCase.java b/boot-script/src/test/java/net/java/html/boot/script/SingleCase.java
new file mode 100644
index 0000000..5ffa79c
--- /dev/null
+++ b/boot-script/src/test/java/net/java/html/boot/script/SingleCase.java
@@ -0,0 +1,127 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.boot.script;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import org.netbeans.html.boot.spi.Fn;
+import org.netbeans.html.boot.impl.FnContext;
+import org.testng.IHookCallBack;
+import org.testng.IHookable;
+import org.testng.ITest;
+import org.testng.ITestResult;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public final class SingleCase implements ITest, IHookable, Runnable {
+    static final Executor JS = Executors.newSingleThreadExecutor();
+    private final Fn.Presenter p;
+    private final Method m;
+    private Object result;
+    private Object inst;
+
+    SingleCase(Fn.Presenter p, Method m) {
+        this.p = p;
+        this.m = m;
+    }
+
+    @Override
+    public String getTestName() {
+        return m.getName();
+    }
+
+    @Test
+    public synchronized void executeTest() throws Exception {
+        if (result == null) {
+            JS.execute(this);
+            wait();
+        }
+        if (result instanceof Exception) {
+            throw (Exception)result;
+        }
+        if (result instanceof Error) {
+            throw (Error)result;
+        }
+    }
+
+    @Override
+    public synchronized void run() {
+        boolean notify = true;
+        try {
+            FnContext.currentPresenter(p);
+            if (inst == null) {
+                inst = m.getDeclaringClass().newInstance();
+            }
+            result = m.invoke(inst);
+            if (result == null) {
+                result = this;
+            }
+        } catch (InvocationTargetException ex) {
+            Throwable r = ex.getTargetException();
+            if (r instanceof InterruptedException) {
+                notify = false;
+                JS.execute(this);
+                return;
+            }
+            result = r;
+        } catch (Exception ex) {
+            result = ex;
+        } finally {
+            if (notify) {
+                notifyAll();
+            }
+            FnContext.currentPresenter(null);
+        }
+    }
+
+    @Override
+    public void run(IHookCallBack ihcb, ITestResult itr) {
+        ihcb.runTestMethod(itr);
+    }
+    
+}



[19/24] incubator-netbeans-html4j git commit: [INFRA-15006] Initial donation of HTML/Java NetBeans API. Equivalent to the content of html4j-donation-review.zip donated as part of ApacheNetBeansDonation1.zip with SHA256 being 7f2ca0f61953a190613c9a0fbcc1b

Posted by jt...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/main/java/org/netbeans/html/boot/impl/FnUtils.java
----------------------------------------------------------------------
diff --git a/boot/src/main/java/org/netbeans/html/boot/impl/FnUtils.java b/boot/src/main/java/org/netbeans/html/boot/impl/FnUtils.java
new file mode 100644
index 0000000..7abae31
--- /dev/null
+++ b/boot/src/main/java/org/netbeans/html/boot/impl/FnUtils.java
@@ -0,0 +1,717 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import net.java.html.js.JavaScriptBody;
+import net.java.html.js.JavaScriptResource;
+import org.netbeans.html.boot.spi.Fn;
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.signature.SignatureReader;
+import org.objectweb.asm.signature.SignatureVisitor;
+import org.objectweb.asm.signature.SignatureWriter;
+
+/** Utilities related to bytecode transformations. Depend on asm.jar which
+ * needs to be added to be provided to classpath to make methods in this 
+ * class useful.
+ *
+ * @author Jaroslav Tulach
+ */
+public final class FnUtils {
+    
+    private FnUtils() {
+    }
+    
+    /** Seeks for {@link JavaScriptBody} and {@link JavaScriptResource} annotations
+     * in the bytecode and converts them into real code. Used by Maven plugin
+     * postprocessing classes.
+     * 
+     * @param bytecode the original bytecode with javascript specific annotations
+     * @param loader the loader to load resources (scripts and classes) when needed
+     * @return the transformed bytecode
+     * @since 0.7
+     */
+    public static byte[] transform(byte[] bytecode, ClassLoader loader) {
+        ClassReader cr = new ClassReader(bytecode) {
+            // to allow us to compile with -profile compact1 on 
+            // JDK8 while processing the class as JDK7, the highest
+            // class format asm 4.1 understands to
+            @Override
+            public short readShort(int index) {
+                short s = super.readShort(index);
+                if (index == 6 && s > Opcodes.V1_7) {
+                    return Opcodes.V1_7;
+                }
+                return s;
+            }
+        };
+        FindInClass tst = new FindInClass(loader, null);
+        cr.accept(tst, 0);
+        if (tst.found > 0) {
+            ClassWriter w = new ClassWriterEx(loader, cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
+            FindInClass fic = new FindInClass(loader, w);
+            cr.accept(fic, 0);
+            bytecode = w.toByteArray();
+        }
+        return bytecode;
+    }
+    
+    public static ClassLoader newLoader(final FindResources f, final Fn.Presenter d, ClassLoader parent) {
+        return new JsClassLoaderImpl(parent, f, d);
+    }
+
+    static String callback(final String body) {
+        return new JsCallback() {
+            @Override
+            protected CharSequence callMethod(
+                String ident, String fqn, String method, String params
+            ) {
+                StringBuilder sb = new StringBuilder();
+                if (ident != null) {
+                    sb.append("vm.raw$");
+                } else {
+                    sb.append("vm.");
+                }
+                sb.append(mangle(fqn, method, params));
+                sb.append("(");
+                if (ident != null) {
+                    sb.append(ident);
+                }
+                return sb;
+            }
+
+        }.parse(body);
+    }
+
+    private static final class FindInClass extends ClassVisitor {
+        private String name;
+        private int found;
+        private String resource;
+
+        public FindInClass(ClassLoader l, ClassVisitor cv) {
+            super(Opcodes.ASM4, cv);
+        }
+
+        @Override
+        public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
+            this.name = name;
+            super.visit(version, access, name, signature, superName, interfaces);
+        }
+
+        @Override
+        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+            final AnnotationVisitor del = super.visitAnnotation(desc, visible);
+            if ("Lnet/java/html/js/JavaScriptResource;".equals(desc)) {
+                return new LoadResource(del);
+            }
+            return del;
+        }
+
+        @Override
+        public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
+            return new FindInMethod(access, name, desc,
+                    super.visitMethod(access & (~Opcodes.ACC_NATIVE), name, desc, signature, exceptions)
+            );
+        }
+
+        @Override
+        public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
+            if (name.startsWith("$$fn$$")) {
+                return null;
+            }
+            return superField(access, name, desc, signature, value);
+        }
+
+        final FieldVisitor superField(int access, String name, String desc, String signature, Object value) {
+            return super.visitField(access, name, desc, signature, value);
+        }
+
+        private final class FindInMethod extends MethodVisitor {
+
+            private final String name;
+            private final String desc;
+            private final int access;
+            private FindInAnno fia;
+            private boolean bodyGenerated;
+
+            public FindInMethod(int access, String name, String desc, MethodVisitor mv) {
+                super(Opcodes.ASM4, mv);
+                this.access = access;
+                this.name = name;
+                this.desc = desc;
+            }
+
+            @Override
+            public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+                if ("Lnet/java/html/js/JavaScriptBody;".equals(desc)) { // NOI18N
+                    found++;
+                    return new FindInAnno();
+                }
+                return super.visitAnnotation(desc, visible);
+            }
+
+            private void generateJSBody(FindInAnno fia) {
+                this.fia = fia;
+            }
+
+            @Override
+            public void visitCode() {
+                if (fia == null) {
+                    return;
+                }
+                generateBody(true);
+            }
+
+            private boolean generateBody(boolean hasCode) {
+                if (bodyGenerated) {
+                    return false;
+                }
+                bodyGenerated = true;
+                if (mv != null) {
+                    AnnotationVisitor va = super.visitAnnotation("Lnet/java/html/js/JavaScriptBody;", false);
+                    AnnotationVisitor varr = va.visitArray("args");
+                    for (String argName : fia.args) {
+                        varr.visit(null, argName);
+                    }
+                    varr.visitEnd();
+                    va.visit("javacall", fia.javacall);
+                    va.visit("body", fia.body);
+                    va.visitEnd();
+                }
+                
+                String body;
+                List<String> args;
+                if (fia.javacall) {
+                    body = callback(fia.body);
+                    args = new ArrayList<String>(fia.args);
+                    args.add("vm");
+                } else {
+                    body = fia.body;
+                    args = fia.args;
+                }
+
+                super.visitFieldInsn(
+                        Opcodes.GETSTATIC, FindInClass.this.name,
+                        "$$fn$$" + name + "_" + found,
+                        "Lorg/netbeans/html/boot/spi/Fn;"
+                );
+                super.visitInsn(Opcodes.DUP);
+                super.visitMethodInsn(
+                        Opcodes.INVOKESTATIC,
+                        "org/netbeans/html/boot/spi/Fn", "isValid",
+                        "(Lorg/netbeans/html/boot/spi/Fn;)Z"
+                );
+                Label ifNotNull = new Label();
+                super.visitJumpInsn(Opcodes.IFNE, ifNotNull);
+
+                // init Fn
+                super.visitInsn(Opcodes.POP);
+                super.visitLdcInsn(Type.getObjectType(FindInClass.this.name));
+                super.visitInsn(fia.keepAlive ? Opcodes.ICONST_1 : Opcodes.ICONST_0);
+                super.visitLdcInsn(body);
+                super.visitIntInsn(Opcodes.SIPUSH, args.size());
+                super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/String");
+                boolean needsVM = false;
+                for (int i = 0; i < args.size(); i++) {
+                    assert !needsVM;
+                    String argName = args.get(i);
+                    needsVM = "vm".equals(argName);
+                    super.visitInsn(Opcodes.DUP);
+                    super.visitIntInsn(Opcodes.BIPUSH, i);
+                    super.visitLdcInsn(argName);
+                    super.visitInsn(Opcodes.AASTORE);
+                }
+                super.visitMethodInsn(Opcodes.INVOKESTATIC,
+                        "org/netbeans/html/boot/spi/Fn", "define",
+                        "(Ljava/lang/Class;ZLjava/lang/String;[Ljava/lang/String;)Lorg/netbeans/html/boot/spi/Fn;"
+                );
+                Label noPresenter = new Label();
+                super.visitInsn(Opcodes.DUP);
+                super.visitJumpInsn(Opcodes.IFNULL, noPresenter);
+                if (resource != null) {
+                    super.visitLdcInsn(Type.getObjectType(FindInClass.this.name));
+                    super.visitLdcInsn(resource);
+                    super.visitMethodInsn(Opcodes.INVOKESTATIC,
+                            "org/netbeans/html/boot/spi/Fn", "preload",
+                            "(Lorg/netbeans/html/boot/spi/Fn;Ljava/lang/Class;Ljava/lang/String;)Lorg/netbeans/html/boot/spi/Fn;"
+                    );
+                }
+                super.visitInsn(Opcodes.DUP);
+                super.visitFieldInsn(
+                        Opcodes.PUTSTATIC, FindInClass.this.name,
+                        "$$fn$$" + name + "_" + found,
+                        "Lorg/netbeans/html/boot/spi/Fn;"
+                );
+                // end of Fn init
+
+                super.visitLabel(ifNotNull);
+
+                final int offset;
+                if ((access & Opcodes.ACC_STATIC) == 0) {
+                    offset = 1;
+                    super.visitIntInsn(Opcodes.ALOAD, 0);
+                } else {
+                    offset = 0;
+                    super.visitInsn(Opcodes.ACONST_NULL);
+                }
+
+                super.visitIntInsn(Opcodes.SIPUSH, args.size());
+                super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
+
+                class SV extends SignatureVisitor {
+
+                    private boolean nowReturn;
+                    private Type returnType;
+                    private int index;
+                    private int loadIndex = offset;
+
+                    public SV() {
+                        super(Opcodes.ASM4);
+                    }
+
+                    @Override
+                    public void visitBaseType(char descriptor) {
+                        final Type t = Type.getType("" + descriptor);
+                        if (nowReturn) {
+                            returnType = t;
+                            return;
+                        }
+                        FindInMethod.super.visitInsn(Opcodes.DUP);
+                        FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index++);
+                        FindInMethod.super.visitVarInsn(t.getOpcode(Opcodes.ILOAD), loadIndex++);
+                        String factory;
+                        switch (descriptor) {
+                            case 'I':
+                                factory = "java/lang/Integer";
+                                break;
+                            case 'J':
+                                factory = "java/lang/Long";
+                                loadIndex++;
+                                break;
+                            case 'S':
+                                factory = "java/lang/Short";
+                                break;
+                            case 'F':
+                                factory = "java/lang/Float";
+                                break;
+                            case 'D':
+                                factory = "java/lang/Double";
+                                loadIndex++;
+                                break;
+                            case 'Z':
+                                factory = "java/lang/Boolean";
+                                break;
+                            case 'C':
+                                factory = "java/lang/Character";
+                                break;
+                            case 'B':
+                                factory = "java/lang/Byte";
+                                break;
+                            default:
+                                throw new IllegalStateException(t.toString());
+                        }
+                        FindInMethod.super.visitMethodInsn(Opcodes.INVOKESTATIC,
+                                factory, "valueOf", "(" + descriptor + ")L" + factory + ";"
+                        );
+                        FindInMethod.super.visitInsn(Opcodes.AASTORE);
+                    }
+
+                    @Override
+                    public SignatureVisitor visitArrayType() {
+                        if (nowReturn) {
+                            return new SignatureVisitor(Opcodes.ASM4) {
+                                @Override
+                                public void visitClassType(String name) {
+                                    returnType = Type.getType("[" + Type.getObjectType(name).getDescriptor());
+                                }
+
+                                @Override
+                                public void visitBaseType(char descriptor) {
+                                    returnType = Type.getType("[" + descriptor);
+                                }
+                            };
+                        }
+                        loadObject();
+                        return new SignatureWriter();
+                    }
+
+                    @Override
+                    public void visitClassType(String name) {
+                        if (nowReturn) {
+                            returnType = Type.getObjectType(name);
+                            return;
+                        }
+                        loadObject();
+                    }
+
+                    @Override
+                    public SignatureVisitor visitReturnType() {
+                        nowReturn = true;
+                        return this;
+                    }
+
+                    private void loadObject() {
+                        FindInMethod.super.visitInsn(Opcodes.DUP);
+                        FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index++);
+                        FindInMethod.super.visitVarInsn(Opcodes.ALOAD, loadIndex++);
+                        FindInMethod.super.visitInsn(Opcodes.AASTORE);
+                    }
+
+                }
+                SV sv = new SV();
+                SignatureReader sr = new SignatureReader(desc);
+                sr.accept(sv);
+
+                if (needsVM) {
+                    FindInMethod.super.visitInsn(Opcodes.DUP);
+                    FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, sv.index);
+                    int lastSlash = FindInClass.this.name.lastIndexOf('/');
+                    String jsCallbacks = FindInClass.this.name.substring(0, lastSlash + 1) + "$JsCallbacks$";
+                    FindInMethod.super.visitFieldInsn(Opcodes.GETSTATIC, jsCallbacks, "VM", "L" + jsCallbacks + ";");
+                    FindInMethod.super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, jsCallbacks, "current", "()L" + jsCallbacks + ";");
+                    FindInMethod.super.visitInsn(Opcodes.AASTORE);
+                }
+
+                if (fia.wait4js) {
+                    super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
+                            "org/netbeans/html/boot/spi/Fn", "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"
+                    );
+                    switch (sv.returnType.getSort()) {
+                        case Type.VOID:
+                            super.visitInsn(Opcodes.RETURN);
+                            break;
+                        case Type.ARRAY:
+                        case Type.OBJECT:
+                            super.visitTypeInsn(Opcodes.CHECKCAST, sv.returnType.getInternalName());
+                            super.visitInsn(Opcodes.ARETURN);
+                            break;
+                        case Type.BOOLEAN: {
+                            Label handleNullValue = new Label();
+                            super.visitInsn(Opcodes.DUP);
+                            super.visitJumpInsn(Opcodes.IFNULL, handleNullValue);
+                            super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Boolean");
+                            super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
+                                    "java/lang/Boolean", "booleanValue", "()Z"
+                            );
+                            super.visitInsn(Opcodes.IRETURN);
+                            super.visitLabel(handleNullValue);
+                            super.visitInsn(Opcodes.ICONST_0);
+                            super.visitInsn(Opcodes.IRETURN);
+                            break;
+                        }
+                        default:
+                            super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Number");
+                            super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
+                                    "java/lang/Number", sv.returnType.getClassName() + "Value", "()" + sv.returnType.getDescriptor()
+                            );
+                            super.visitInsn(sv.returnType.getOpcode(Opcodes.IRETURN));
+                    }
+                } else {
+                    super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
+                            "org/netbeans/html/boot/spi/Fn", "invokeLater", "(Ljava/lang/Object;[Ljava/lang/Object;)V"
+                    );
+                    super.visitInsn(Opcodes.RETURN);
+                }
+                super.visitLabel(noPresenter);
+                if (hasCode) {
+                    super.visitCode();
+                } else {
+                    super.visitTypeInsn(Opcodes.NEW, "java/lang/IllegalStateException");
+                    super.visitInsn(Opcodes.DUP);
+                    super.visitLdcInsn("No presenter active. Use BrwsrCtx.execute!");
+                    super.visitMethodInsn(Opcodes.INVOKESPECIAL, 
+                        "java/lang/IllegalStateException", "<init>", "(Ljava/lang/String;)V"
+                    );
+                    this.visitInsn(Opcodes.ATHROW);
+                }
+                return true;
+            }
+            
+            @Override
+            public void visitEnd() {
+                super.visitEnd();
+                if (fia != null) {
+                    if (generateBody(false)) {
+                        // native method
+                        super.visitMaxs(1, 0);
+                    }
+                    FindInClass.this.superField(
+                            Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC,
+                            "$$fn$$" + name + "_" + found,
+                            "Lorg/netbeans/html/boot/spi/Fn;",
+                            null, null
+                    );
+                }
+            }
+
+            private final class FindInAnno extends AnnotationVisitor {
+
+                List<String> args = new ArrayList<String>();
+                String body;
+                boolean javacall = false;
+                boolean wait4js = true;
+                boolean keepAlive = true;
+
+                public FindInAnno() {
+                    super(Opcodes.ASM4);
+                }
+
+                @Override
+                public void visit(String name, Object value) {
+                    if (name == null) {
+                        args.add((String) value);
+                        return;
+                    }
+                    if (name.equals("javacall")) { // NOI18N
+                        javacall = (Boolean) value;
+                        return;
+                    }
+                    if (name.equals("wait4js")) { // NOI18N
+                        wait4js = (Boolean) value;
+                        return;
+                    }
+                    if (name.equals("keepAlive")) { // NOI18N
+                        keepAlive = (Boolean) value;
+                        return;
+                    }
+                    assert name.equals("body"); // NOI18N
+                    body = (String) value;
+                }
+
+                @Override
+                public AnnotationVisitor visitArray(String name) {
+                    return this;
+                }
+
+                @Override
+                public void visitEnd() {
+                    if (body != null) {
+                        generateJSBody(this);
+                    }
+                }
+            }
+        }
+
+        private final class LoadResource extends AnnotationVisitor {
+            public LoadResource(AnnotationVisitor av) {
+                super(Opcodes.ASM4, av);
+            }
+
+            @Override
+            public void visit(String attrName, Object value) {
+                super.visit(attrName, value);
+                String relPath = (String) value;
+                if (relPath.startsWith("/")) {
+                    resource = relPath;
+                } else {
+                    int last = name.lastIndexOf('/');
+                    String fullPath = name.substring(0, last + 1) + relPath;
+                    resource = fullPath;
+                }
+            }
+        }
+    }
+
+    private static class ClassWriterEx extends ClassWriter {
+
+        private final ClassLoader loader;
+
+        public ClassWriterEx(ClassLoader l, ClassReader classReader, int flags) {
+            super(classReader, flags);
+            this.loader = l;
+        }
+
+        @Override
+        protected String getCommonSuperClass(final String type1, final String type2) {
+            Class<?> c, d;
+            try {
+                c = Class.forName(type1.replace('/', '.'), false, loader);
+                d = Class.forName(type2.replace('/', '.'), false, loader);
+            } catch (Exception e) {
+                throw new RuntimeException(e.toString());
+            }
+            if (c.isAssignableFrom(d)) {
+                return type1;
+            }
+            if (d.isAssignableFrom(c)) {
+                return type2;
+            }
+            if (c.isInterface() || d.isInterface()) {
+                return "java/lang/Object";
+            } else {
+                do {
+                    c = c.getSuperclass();
+                } while (!c.isAssignableFrom(d));
+                return c.getName().replace('.', '/');
+            }
+        }
+    }
+
+    static class JsClassLoaderImpl extends JsClassLoader {
+
+        private final FindResources f;
+        private final Fn.Presenter d;
+
+        public JsClassLoaderImpl(ClassLoader parent, FindResources f, Fn.Presenter d) {
+            super(parent);
+            setDefaultAssertionStatus(JsClassLoader.class.desiredAssertionStatus());
+            this.f = f;
+            this.d = d;
+        }
+
+        @Override
+        protected URL findResource(String name) {
+            List<URL> l = res(name, true);
+            return l.isEmpty() ? null : l.get(0);
+        }
+
+        @Override
+        protected Enumeration<URL> findResources(String name) {
+            return Collections.enumeration(res(name, false));
+        }
+        
+        private List<URL> res(String name, boolean oneIsEnough) {
+            List<URL> l = new ArrayList<URL>();
+            f.findResources(name, l, oneIsEnough);
+            return l;
+        }
+    
+        @Override
+        protected Class<?> findClass(String name) throws ClassNotFoundException {
+            if (name.startsWith("javafx")) {
+                return Class.forName(name);
+            }
+            if (name.startsWith("netscape")) {
+                return Class.forName(name);
+            }
+            if (name.startsWith("com.sun")) {
+                return Class.forName(name);
+            }
+            if (name.startsWith("org.netbeans.html.context.spi")) {
+                return Class.forName(name);
+            }
+            if (name.startsWith("net.java.html.BrwsrCtx")) {
+                return Class.forName(name);
+            }
+            if (name.equals(JsClassLoader.class.getName())) {
+                return JsClassLoader.class;
+            }
+            if (name.equals(Fn.class.getName())) {
+                return Fn.class;
+            }
+            if (name.equals(Fn.Presenter.class.getName())) {
+                return Fn.Presenter.class;
+            }
+            if (name.equals(Fn.ToJavaScript.class.getName())) {
+                return Fn.ToJavaScript.class;
+            }
+            if (name.equals(Fn.FromJavaScript.class.getName())) {
+                return Fn.FromJavaScript.class;
+            }
+            if (name.equals(FnUtils.class.getName())) {
+                return FnUtils.class;
+            }
+            if (
+                name.equals("org.netbeans.html.boot.spi.Fn") ||
+                name.equals("org.netbeans.html.boot.impl.FnUtils") ||
+                name.equals("org.netbeans.html.boot.impl.FnContext")
+            ) {
+                return Class.forName(name);
+            }
+            URL u = findResource(name.replace('.', '/') + ".class");
+            if (u != null) {
+                InputStream is = null;
+                try {
+                    is = u.openStream();
+                    byte[] arr = new byte[is.available()];
+                    int len = 0;
+                    while (len < arr.length) {
+                        int read = is.read(arr, len, arr.length - len);
+                        if (read == -1) {
+                            throw new IOException("Can't read " + u);
+                        }
+                        len += read;
+                    }
+                    is.close();
+                    is = null;
+                    if (JsPkgCache.process(this, name)) {
+                        arr = FnUtils.transform(arr, this);
+                    }
+                    return defineClass(name, arr, 0, arr.length);
+                } catch (IOException ex) {
+                    throw new ClassNotFoundException("Can't load " + name, ex);
+                } finally {
+                    try {
+                        if (is != null) is.close();
+                    } catch (IOException ex) {
+                        throw new ClassNotFoundException(null, ex);
+                    }
+                }
+            }
+            return super.findClass(name);
+        }
+    
+        protected Fn defineFn(String code, String... names) {
+            return d.defineFn(code, names);
+        }
+        
+        protected void loadScript(Reader code) throws Exception {
+            d.loadScript(code);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java
----------------------------------------------------------------------
diff --git a/boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java b/boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java
new file mode 100644
index 0000000..ce9c4bb
--- /dev/null
+++ b/boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java
@@ -0,0 +1,528 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.impl;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.Completion;
+import javax.annotation.processing.Completions;
+import javax.annotation.processing.Messager;
+import javax.annotation.processing.Processor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Types;
+import javax.tools.Diagnostic;
+import javax.tools.FileObject;
+import javax.tools.StandardLocation;
+import net.java.html.js.JavaScriptBody;
+import net.java.html.js.JavaScriptResource;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+@ServiceProvider(service = Processor.class)
+public final class JavaScriptProcesor extends AbstractProcessor {
+    private final Map<String,Map<String,ExecutableElement>> javacalls =
+        new HashMap<String,Map<String,ExecutableElement>>();
+    private final Map<String,Set<TypeElement>> bodies =
+        new HashMap<String, Set<TypeElement>>();
+
+    @Override
+    public Set<String> getSupportedAnnotationTypes() {
+        Set<String> set = new HashSet<String>();
+        set.add(JavaScriptBody.class.getName());
+        set.add(JavaScriptResource.class.getName());
+        return set;
+    }
+
+    @Override
+    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+        final Messager msg = processingEnv.getMessager();
+        for (Element e : roundEnv.getElementsAnnotatedWith(JavaScriptBody.class)) {
+            if (e.getKind() != ElementKind.METHOD && e.getKind() != ElementKind.CONSTRUCTOR) {
+                continue;
+            }
+            ExecutableElement ee = (ExecutableElement)e;
+            List<? extends VariableElement> params = ee.getParameters();
+
+            JavaScriptBody jsb = e.getAnnotation(JavaScriptBody.class);
+            if (jsb == null) {
+                continue;
+            } else {
+                Set<TypeElement> classes = this.bodies.get(findPkg(e));
+                if (classes == null) {
+                    classes = new HashSet<TypeElement>();
+                    bodies.put(findPkg(e), classes);
+                }
+                Element t = e.getEnclosingElement();
+                while (!t.getKind().isClass() && !t.getKind().isInterface()) {
+                    t = t.getEnclosingElement();
+                }
+                classes.add((TypeElement)t);
+            }
+            String[] arr = jsb.args();
+            if (params.size() != arr.length) {
+                msg.printMessage(Diagnostic.Kind.ERROR, "Number of args arguments does not match real arguments!", e);
+            }
+            for (int i = 0; i < arr.length; i++) {
+                if (!params.get(i).getSimpleName().toString().equals(arr[i])) {
+                    msg.printMessage(Diagnostic.Kind.WARNING, "Actual method parameter names and args ones " + Arrays.toString(arr) + " differ", e);
+                }
+            }
+            if (!jsb.wait4js() && ee.getReturnType().getKind() != TypeKind.VOID) {
+                msg.printMessage(Diagnostic.Kind.ERROR, "Methods that don't wait for JavaScript to finish must return void!", e);
+            }
+            if (!jsb.javacall() && jsb.body().contains(".@")) {
+                msg.printMessage(Diagnostic.Kind.WARNING, "Usage of .@ usually requires javacall=true", e);
+            }
+            if (jsb.javacall()) {
+                JsCallback verify = new VerifyCallback(e);
+                try {
+                    verify.parse(jsb.body());
+                } catch (IllegalStateException ex) {
+                    msg.printMessage(Diagnostic.Kind.ERROR, ex.getLocalizedMessage(), e);
+                }
+            }
+        }
+        for (Element e : roundEnv.getElementsAnnotatedWith(JavaScriptResource.class)) {
+            JavaScriptResource r = e.getAnnotation(JavaScriptResource.class);
+            if (r == null) {
+                continue;
+            }
+            final String res;
+            if (r.value().startsWith("/")) {
+                res = r.value().substring(1);
+            } else {
+                res = findPkg(e).replace('.', '/') + "/" + r.value();
+            }
+
+            try {
+                FileObject os = processingEnv.getFiler().getResource(StandardLocation.SOURCE_PATH, "", res);
+                os.openInputStream().close();
+            } catch (IOException ex1) {
+                try {
+                    FileObject os2 = processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", res);
+                    os2.openInputStream().close();
+                } catch (IOException ex2) {
+                    try {
+                        FileObject os3 = processingEnv.getFiler().getResource(StandardLocation.CLASS_PATH, "", res);
+                        os3.openInputStream().close();
+                    } catch (IOException ex3) {
+                        msg.printMessage(Diagnostic.Kind.ERROR, "Cannot find resource " + res, e);
+                    }
+                }
+            }
+
+            boolean found = false;
+            for (Element mthod : e.getEnclosedElements()) {
+                if (mthod.getKind() != ElementKind.METHOD) {
+                    continue;
+                }
+                if (mthod.getAnnotation(JavaScriptBody.class) != null) {
+                    found = true;
+                    break;
+                }
+            }
+            if (!found) {
+                msg.printMessage(Diagnostic.Kind.ERROR, "At least one method needs @JavaScriptBody annotation. "
+                    + "Otherwise it is not guaranteed the resource will ever be loaded,", e
+                );
+            }
+        }
+
+        if (roundEnv.processingOver()) {
+            generateCallbackClass(javacalls);
+            generateJavaScriptBodyList(bodies);
+            javacalls.clear();
+        }
+        return true;
+    }
+
+    @Override
+    public Iterable<? extends Completion> getCompletions(Element e,
+        AnnotationMirror annotation, ExecutableElement member, String userText
+    ) {
+        StringBuilder sb = new StringBuilder();
+        if (e.getKind() == ElementKind.METHOD && member.getSimpleName().contentEquals("args")) {
+            ExecutableElement ee = (ExecutableElement) e;
+            String sep = "";
+            sb.append("{ ");
+            for (VariableElement ve : ee.getParameters()) {
+                sb.append(sep).append('"').append(ve.getSimpleName())
+                    .append('"');
+                sep = ", ";
+            }
+            sb.append(" }");
+            return Collections.nCopies(1, Completions.of(sb.toString()));
+        }
+        return null;
+    }
+
+    private class VerifyCallback extends JsCallback {
+        private final Element e;
+        public VerifyCallback(Element e) {
+            this.e = e;
+        }
+
+        @Override
+        protected CharSequence callMethod(String ident, String fqn, String method, String params) {
+            final TypeElement type = processingEnv.getElementUtils().getTypeElement(fqn);
+            if (type == null) {
+                processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
+                    "Callback to non-existing class " + fqn, e
+                );
+                return "";
+            }
+            ExecutableElement found = null;
+            StringBuilder foundParams = new StringBuilder();
+            for (Element m : type.getEnclosedElements()) {
+                if (m.getKind() != ElementKind.METHOD) {
+                    continue;
+                }
+                if (m.getSimpleName().contentEquals(method)) {
+                    String paramTypes = findParamTypes((ExecutableElement)m);
+                    if (paramTypes.equals(params)) {
+                        found = (ExecutableElement) m;
+                        break;
+                    }
+                    foundParams.append(paramTypes).append("\n");
+                }
+            }
+            if (found == null) {
+                if (foundParams.length() == 0) {
+                    processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
+                        "Callback to class " + fqn + " with unknown method " + method, e
+                    );
+                } else {
+                    processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
+                        "Callback to " + fqn + "." + method + " with wrong parameters: " +
+                        params + ". Only known parameters are " + foundParams, e
+                    );
+                }
+            } else {
+                Map<String,ExecutableElement> mangledOnes = javacalls.get(findPkg(e));
+                if (mangledOnes == null) {
+                    mangledOnes = new TreeMap<String, ExecutableElement>();
+                    javacalls.put(findPkg(e), mangledOnes);
+                }
+                String mangled = JsCallback.mangle(fqn, method, findParamTypes(found));
+                mangledOnes.put(mangled, found);
+            }
+            return "";
+        }
+
+        private String findParamTypes(ExecutableElement method) {
+            ExecutableType t = (ExecutableType) method.asType();
+            StringBuilder sb = new StringBuilder();
+            sb.append('(');
+            for (TypeMirror tm : t.getParameterTypes()) {
+                if (tm.getKind().isPrimitive()) {
+                    switch (tm.getKind()) {
+                        case INT: sb.append('I'); break;
+                        case BOOLEAN: sb.append('Z'); break;
+                        case BYTE: sb.append('B'); break;
+                        case CHAR: sb.append('C'); break;
+                        case SHORT: sb.append('S'); break;
+                        case DOUBLE: sb.append('D'); break;
+                        case FLOAT: sb.append('F'); break;
+                        case LONG: sb.append('J'); break;
+                        default:
+                            throw new IllegalStateException("Uknown " + tm.getKind());
+                    }
+                } else {
+                    while (tm.getKind() == TypeKind.ARRAY) {
+                        sb.append('[');
+                        tm = ((ArrayType)tm).getComponentType();
+                    }
+                    sb.append('L');
+                    Types tu = processingEnv.getTypeUtils();
+                    Element elm = tu.asElement(tu.erasure(tm));
+                    dumpElems(sb, elm, ';');
+                }
+            }
+            sb.append(')');
+            return sb.toString();
+        }
+    }
+
+    private static void dumpElems(StringBuilder sb, Element e, char after) {
+        if (e == null) {
+            return;
+        }
+        if (e.getKind() == ElementKind.PACKAGE) {
+            PackageElement pe = (PackageElement) e;
+            sb.append(pe.getQualifiedName().toString().replace('.', '/')).append('/');
+            return;
+        }
+        Element p = e.getEnclosingElement();
+        dumpElems(sb, p, '$');
+        sb.append(e.getSimpleName());
+        sb.append(after);
+    }
+
+    private void generateJavaScriptBodyList(Map<String,Set<TypeElement>> bodies) {
+        if (bodies.isEmpty()) {
+            return;
+        }
+        try {
+            FileObject all = processingEnv.getFiler().createResource(
+                StandardLocation.CLASS_OUTPUT, "", "META-INF/net.java.html.js.classes"
+            );
+            PrintWriter wAll = new PrintWriter(new OutputStreamWriter(
+                all.openOutputStream(), "UTF-8"
+            ));
+            for (Map.Entry<String, Set<TypeElement>> entry : bodies.entrySet()) {
+                String pkg = entry.getKey();
+                Set<TypeElement> classes = entry.getValue();
+
+                FileObject out = processingEnv.getFiler().createResource(
+                    StandardLocation.CLASS_OUTPUT, pkg, "net.java.html.js.classes",
+                    classes.iterator().next()
+                );
+                OutputStream os = out.openOutputStream();
+                try {
+                    PrintWriter w = new PrintWriter(new OutputStreamWriter(os, "UTF-8"));
+                    for (TypeElement type : classes) {
+                        final Name bn = processingEnv.getElementUtils().getBinaryName(type);
+                        w.println(bn);
+                        wAll.println(bn);
+                    }
+                    w.flush();
+                    w.close();
+                } catch (IOException x) {
+                    processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Failed to write to " + entry.getKey() + ": " + x.toString());
+                } finally {
+                    os.close();
+                }
+            }
+            wAll.close();
+        } catch (IOException x) {
+            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Failed to write to " + "META-INF/net.java.html.js.classes: " + x.toString());
+        }
+    }
+
+    private void generateCallbackClass(Map<String,Map<String, ExecutableElement>> process) {
+        for (Map.Entry<String, Map<String, ExecutableElement>> pkgEn : process.entrySet()) {
+            String pkgName = pkgEn.getKey();
+            Map<String, ExecutableElement> map = pkgEn.getValue();
+            StringBuilder source = new StringBuilder();
+            source.append("package ").append(pkgName).append(";\n");
+            source.append("public final class $JsCallbacks$ {\n");
+            source.append("  static final $JsCallbacks$ VM = new $JsCallbacks$(null);\n");
+            source.append("  private final org.netbeans.html.boot.spi.Fn.Presenter p;\n");
+            source.append("  private $JsCallbacks$ last;\n");
+            source.append("  private $JsCallbacks$(org.netbeans.html.boot.spi.Fn.Presenter p) {\n");
+            source.append("    this.p = p;\n");
+            source.append("  }\n");
+            source.append("  final $JsCallbacks$ current() {\n");
+            source.append("    org.netbeans.html.boot.spi.Fn.Presenter now = org.netbeans.html.boot.spi.Fn.activePresenter();\n");
+            source.append("    if (now == p) return this;\n");
+            source.append("    if (last != null && now == last.p) return last;\n");
+            source.append("    return last = new $JsCallbacks$(now);\n");
+            source.append("  }\n");
+            for (Map.Entry<String, ExecutableElement> entry : map.entrySet()) {
+                final String mangled = entry.getKey();
+                final ExecutableElement m = entry.getValue();
+                generateMethod(false, m, source, mangled);
+                generateMethod(true, m, source, "raw$" + mangled);
+            }
+            source.append("}\n");
+            final String srcName = pkgName + ".$JsCallbacks$";
+            try {
+                Writer w = processingEnv.getFiler().createSourceFile(srcName,
+                    map.values().toArray(new Element[map.size()])
+                ).openWriter();
+                w.write(source.toString());
+                w.close();
+            } catch (IOException ex) {
+                processingEnv.getMessager().printMessage(
+                    Diagnostic.Kind.ERROR, "Can't write " + srcName + ": " + ex.getMessage()
+                );
+            }
+        }
+    }
+
+    private void generateMethod(boolean selfObj, final ExecutableElement m, StringBuilder source, final String mangled) {
+        final boolean isStatic = m.getModifiers().contains(Modifier.STATIC);
+        if (isStatic && selfObj) {
+            return;
+        }
+        final TypeElement selfType = (TypeElement)m.getEnclosingElement();
+        Types tu = processingEnv.getTypeUtils();
+
+        source.append("\n  public java.lang.Object ")
+                .append(mangled)
+                .append("(");
+
+        String sep = "";
+        StringBuilder convert = new StringBuilder();
+        if (!isStatic) {
+            if (selfObj) {
+                source.append("java.lang.Object self");
+                convert.append("    if (p instanceof org.netbeans.html.boot.spi.Fn.FromJavaScript) {\n");
+                convert.append("      self").
+                        append(" = ((org.netbeans.html.boot.spi.Fn.FromJavaScript)p).toJava(self").
+                        append(");\n");
+                convert.append("    }\n");
+            } else {
+                source.append(selfType.getQualifiedName());
+                source.append(" self");
+            }
+            sep = ", ";
+        }
+
+        int cnt = 0;
+        for (VariableElement ve : m.getParameters()) {
+            source.append(sep);
+            ++cnt;
+            final TypeMirror t = ve.asType();
+            if (!t.getKind().isPrimitive() && !"java.lang.String".equals(t.toString())) { // NOI18N
+                source.append("java.lang.Object");
+                convert.append("    if (p instanceof org.netbeans.html.boot.spi.Fn.FromJavaScript) {\n");
+                convert.append("      arg").append(cnt).
+                        append(" = ((org.netbeans.html.boot.spi.Fn.FromJavaScript)p).toJava(arg").append(cnt).
+                        append(");\n");
+                convert.append("    }\n");
+            } else {
+                source.append(t);
+            }
+            source.append(" arg").append(cnt);
+            sep = ", ";
+        }
+        source.append(") throws Throwable {\n");
+        source.append(convert);
+        if (useTryResources()) {
+            source.append("    try (java.io.Closeable a = org.netbeans.html.boot.spi.Fn.activate(p)) { \n");
+        } else {
+            source.append("    java.io.Closeable a = org.netbeans.html.boot.spi.Fn.activate(p); try {\n");
+        }
+        source.append("    ");
+        if (m.getReturnType().getKind() != TypeKind.VOID) {
+            source.append("java.lang.Object $ret = ");
+        }
+        if (isStatic) {
+            source.append(((TypeElement)m.getEnclosingElement()).getQualifiedName());
+            source.append('.');
+        } else {
+            if (selfObj) {
+                source.append("((");
+                source.append(selfType.getQualifiedName());
+                source.append(")self).");
+            } else {
+                source.append("self.");
+            }
+        }
+        source.append(m.getSimpleName());
+        source.append("(");
+        cnt = 0;
+        sep = "";
+        for (VariableElement ve : m.getParameters()) {
+            source.append(sep);
+            source.append("(").append(tu.erasure(ve.asType()));
+            source.append(")arg").append(++cnt);
+            sep = ", ";
+        }
+        source.append(");\n");
+        if (m.getReturnType().getKind() == TypeKind.VOID) {
+            source.append("    return null;\n");
+        } else {
+            source.append("    if (p instanceof org.netbeans.html.boot.spi.Fn.ToJavaScript) {\n");
+            source.append("      $ret = ((org.netbeans.html.boot.spi.Fn.ToJavaScript)p).toJavaScript($ret);\n");
+            source.append("    }\n");
+            source.append("    return $ret;\n");
+        }
+        if (useTryResources()) {
+            source.append("    }\n");
+        } else {
+
+            source.append("    } finally {\n");
+            source.append("      a.close();\n");
+            source.append("    }\n");
+        }
+        source.append("  }\n");
+    }
+
+    private boolean useTryResources() {
+        try {
+            return processingEnv.getSourceVersion().compareTo(SourceVersion.RELEASE_7) >= 0;
+        } catch (LinkageError err) {
+            // can happen when running on JDK6
+            return false;
+        }
+    }
+
+    private static String findPkg(Element e) {
+        while (e.getKind() != ElementKind.PACKAGE) {
+            e = e.getEnclosingElement();
+        }
+        return ((PackageElement)e).getQualifiedName().toString();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/main/java/org/netbeans/html/boot/impl/JsAgent.java
----------------------------------------------------------------------
diff --git a/boot/src/main/java/org/netbeans/html/boot/impl/JsAgent.java b/boot/src/main/java/org/netbeans/html/boot/impl/JsAgent.java
new file mode 100644
index 0000000..5d72fa2
--- /dev/null
+++ b/boot/src/main/java/org/netbeans/html/boot/impl/JsAgent.java
@@ -0,0 +1,75 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.impl;
+
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.IllegalClassFormatException;
+import java.lang.instrument.Instrumentation;
+import java.security.ProtectionDomain;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public final class JsAgent implements ClassFileTransformer {
+    public static void premain(String args, Instrumentation instr) {
+        instr.addTransformer(new JsAgent());
+    }
+    
+    public static void agentmain(String args, Instrumentation instr) {
+        instr.addTransformer(new JsAgent());
+    }
+
+    @Override
+    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
+        try {
+            if (JsPkgCache.process(loader, className)) {
+                return FnUtils.transform(classfileBuffer, loader);
+            } else {
+                return classfileBuffer;
+            }
+        } catch (Exception ex) {
+            return classfileBuffer;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/main/java/org/netbeans/html/boot/impl/JsCallback.java
----------------------------------------------------------------------
diff --git a/boot/src/main/java/org/netbeans/html/boot/impl/JsCallback.java b/boot/src/main/java/org/netbeans/html/boot/impl/JsCallback.java
new file mode 100644
index 0000000..45bf53b
--- /dev/null
+++ b/boot/src/main/java/org/netbeans/html/boot/impl/JsCallback.java
@@ -0,0 +1,160 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.impl;
+
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+abstract class JsCallback {
+    final String parse(String body) {
+        StringBuilder sb = new StringBuilder();
+        int pos = 0;
+        for (;;) {
+            int next = body.indexOf(".@", pos);
+            if (next == -1) {
+                sb.append(body.substring(pos));
+                body = sb.toString();
+                break;
+            }
+            int ident = next;
+            while (ident > 0) {
+                if (!Character.isJavaIdentifierPart(body.charAt(--ident))) {
+                    ident++;
+                    break;
+                }
+            }
+            String refId = body.substring(ident, next);
+            
+            sb.append(body.substring(pos, ident));
+            
+            int sigBeg = body.indexOf('(', next);
+            int sigEnd = body.indexOf(')', sigBeg);
+            int colon4 = body.indexOf("::", next);
+            if (sigBeg == -1 || sigEnd == -1 || colon4 == -1) {
+                throw new IllegalStateException(
+                    "Wrong format of instance callback. "
+                    + "Should be: 'inst.@pkg.Class::method(Ljava/lang/Object;)(param)':\n" 
+                    + body
+                );
+            }
+            String fqn = body.substring(next + 2, colon4);
+            String method = body.substring(colon4 + 2, sigBeg);
+            String params = body.substring(sigBeg, sigEnd + 1);
+
+            int paramBeg = body.indexOf('(', sigEnd + 1);
+            if (paramBeg == -1) {
+                throw new IllegalStateException(
+                    "Wrong format of instance callback. "
+                    + "Should be: 'inst.@pkg.Class::method(Ljava/lang/Object;)(param)':\n" 
+                    + body
+                );
+            }
+            
+            sb.append(callMethod(refId, fqn, method, params));
+            if (body.charAt(paramBeg + 1) != (')')) {
+                sb.append(",");
+            }
+            pos = paramBeg + 1;
+        }
+        pos = 0;
+        sb = null;
+        for (;;) {
+            int next = body.indexOf("@", pos);
+            if (next == -1) {
+                if (sb == null) {
+                    return body;
+                }
+                sb.append(body.substring(pos));
+                return sb.toString();
+            }
+            if (sb == null) {
+                sb = new StringBuilder();
+            }
+            
+            sb.append(body.substring(pos, next));
+            
+            int sigBeg = body.indexOf('(', next);
+            int sigEnd = body.indexOf(')', sigBeg);
+            int colon4 = body.indexOf("::", next);
+            int paramBeg = body.indexOf('(', sigEnd + 1);
+            if (sigBeg == -1 || sigEnd == -1 || colon4 == -1 || paramBeg == -1) {
+                throw new IllegalStateException(
+                    "Wrong format of static callback. "
+                    + "Should be: '@pkg.Class::staticMethod(Ljava/lang/Object;)(param)':\n" 
+                    + body
+                );
+            }
+            String fqn = body.substring(next + 1, colon4);
+            String method = body.substring(colon4 + 2, sigBeg);
+            String params = body.substring(sigBeg, sigEnd + 1);
+
+            
+            sb.append(callMethod(null, fqn, method, params));
+            pos = paramBeg + 1;
+        }
+    }
+
+    protected abstract CharSequence callMethod(
+        String ident, String fqn, String method, String params
+    );
+
+    static String mangle(String fqn, String method, String params) {
+        if (params.startsWith("(")) {
+            params = params.substring(1);
+        }
+        if (params.endsWith(")")) {
+            params = params.substring(0, params.length() - 1);
+        }
+        return 
+            replace(fqn) + "$" + replace(method) + "$" + replace(params);
+    }
+    
+    private static String replace(String orig) {
+        return orig.replace("_", "_1").
+            replace(";", "_2").
+            replace("[", "_3").
+            replace('.', '_').replace('/', '_');
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/main/java/org/netbeans/html/boot/impl/JsClassLoader.java
----------------------------------------------------------------------
diff --git a/boot/src/main/java/org/netbeans/html/boot/impl/JsClassLoader.java b/boot/src/main/java/org/netbeans/html/boot/impl/JsClassLoader.java
new file mode 100644
index 0000000..58b0a62
--- /dev/null
+++ b/boot/src/main/java/org/netbeans/html/boot/impl/JsClassLoader.java
@@ -0,0 +1,57 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.impl;
+
+import net.java.html.js.JavaScriptBody;
+
+/** Marker class to help us recognize we assigned classloader is
+ * capable to handle {@link JavaScriptBody} annotated methods.
+ *
+ * @author Jaroslav Tulach
+ */
+abstract class JsClassLoader extends ClassLoader {
+    JsClassLoader(ClassLoader parent) {
+        super(parent);
+        setDefaultAssertionStatus(JsClassLoader.class.desiredAssertionStatus());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/main/java/org/netbeans/html/boot/impl/JsPkgCache.java
----------------------------------------------------------------------
diff --git a/boot/src/main/java/org/netbeans/html/boot/impl/JsPkgCache.java b/boot/src/main/java/org/netbeans/html/boot/impl/JsPkgCache.java
new file mode 100644
index 0000000..7064d57
--- /dev/null
+++ b/boot/src/main/java/org/netbeans/html/boot/impl/JsPkgCache.java
@@ -0,0 +1,132 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.boot.impl;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.WeakHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+final class JsPkgCache {
+    private final Map<String,Set<String>> props = new WeakHashMap<String, Set<String>>();
+    private static final Map<ClassLoader, JsPkgCache> CACHE = new WeakHashMap<ClassLoader, JsPkgCache>();
+    private static final Set<String> NONE = Collections.emptySet();
+
+    public static boolean process(ClassLoader l, String className) {
+        if (className.equals("org.netbeans.html.boot.impl.Test")) { // NOI18N
+            return true;
+        }
+        Set<String> p;
+        JsPkgCache c;
+        String pkgName;
+        synchronized (CACHE) {
+            c = CACHE.get(l);
+            if (c == null) {
+                c = new JsPkgCache();
+                CACHE.put(l, c);
+            }
+            int lastDot = className.lastIndexOf('.');
+            pkgName = className.substring(0, lastDot + 1).replace('.', '/');
+            p = c.props.get(pkgName);
+            if (p == NONE) {
+                return false;
+            } else if (p != null) {
+                return p.contains(className);
+            }
+        }
+        final String res = pkgName + "net.java.html.js.classes";
+        
+        Enumeration<URL> en;
+        try {
+            en = l.getResources(res);
+        } catch (IOException ex) {
+            en = null;
+        }
+        if (en == null || !en.hasMoreElements()) synchronized (CACHE) {
+            c.props.put(pkgName, NONE);
+            return false;
+        }
+
+        try {
+            Set<String> arr = new TreeSet<String>();
+            while (en.hasMoreElements()) {
+                URL u = en.nextElement();
+                BufferedReader r = new BufferedReader(
+                    new InputStreamReader(u.openStream(), "UTF-8")
+                );
+                for (;;) {
+                    String line = r.readLine();
+                    if (line == null) {
+                        break;
+                    }
+                    arr.add(line);
+                }
+                r.close();
+            }
+            p = arr;
+        } catch (IOException ex) {
+            LOG.log(Level.WARNING, "Can't read " + res, ex);
+            p = NONE;
+        }
+        
+        synchronized (CACHE) {
+            c.props.put(pkgName, p);
+            return p.contains(className);
+        }
+        
+    }
+    private static final Logger LOG = Logger.getLogger(JsPkgCache.class.getName());
+}


[12/24] incubator-netbeans-html4j git commit: [INFRA-15006] Initial donation of HTML/Java NetBeans API. Equivalent to the content of html4j-donation-review.zip donated as part of ApacheNetBeansDonation1.zip with SHA256 being 7f2ca0f61953a190613c9a0fbcc1b

Posted by jt...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json-tck/src/main/java/org/netbeans/html/json/tck/KOTest.java
----------------------------------------------------------------------
diff --git a/json-tck/src/main/java/org/netbeans/html/json/tck/KOTest.java b/json-tck/src/main/java/org/netbeans/html/json/tck/KOTest.java
new file mode 100644
index 0000000..9caf123
--- /dev/null
+++ b/json-tck/src/main/java/org/netbeans/html/json/tck/KOTest.java
@@ -0,0 +1,60 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.json.tck;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** Annotates method that is part of {@link KnockoutTCK test compatibility kit} 
+ * and should be executed in appropriate environment. The method annotated by
+ * this annotation will be public instance method of its class 
+ * with no arguments.
+ *
+ * @author Jaroslav Tulach
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface KOTest {
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json-tck/src/main/java/org/netbeans/html/json/tck/KnockoutTCK.java
----------------------------------------------------------------------
diff --git a/json-tck/src/main/java/org/netbeans/html/json/tck/KnockoutTCK.java b/json-tck/src/main/java/org/netbeans/html/json/tck/KnockoutTCK.java
new file mode 100644
index 0000000..6bf968a
--- /dev/null
+++ b/json-tck/src/main/java/org/netbeans/html/json/tck/KnockoutTCK.java
@@ -0,0 +1,144 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.json.tck;
+
+import java.net.URI;
+import java.util.Map;
+import net.java.html.BrwsrCtx;
+import net.java.html.json.tests.ConvertTypesTest;
+import net.java.html.json.tests.GCKnockoutTest;
+import net.java.html.json.tests.JSONTest;
+import net.java.html.json.tests.KnockoutTest;
+import net.java.html.json.tests.MinesTest;
+import net.java.html.json.tests.OperationsTest;
+import net.java.html.json.tests.Utils;
+import net.java.html.json.tests.WebSocketTest;
+import org.netbeans.html.context.spi.Contexts.Builder;
+import org.openide.util.lookup.ServiceProvider;
+import org.testng.annotations.Factory;
+
+/** Entry point for providers of different HTML binding technologies (like
+ * Knockout.js in JavaFX's WebView). Sample usage:
+ * 
+<pre>
+{@link ServiceProvider @ServiceProvider}(service = KnockoutTCK.class)
+public final class MyKnockoutBindingTest extends KnockoutTCK {
+    {@link Override @Override}
+    protected BrwsrCtx createContext() {
+        // use {@link Builder}.{@link Builder#build() build}();
+    }
+
+    {@code @}{@link Factory} public static Object[] create() {
+        return VMTest.newTests().withClasses({@link KnockoutTCK#testClasses}()).build();
+    }
+}
+ * </pre>
+ *
+ * @author Jaroslav Tulach
+ */
+public abstract class KnockoutTCK {
+    protected KnockoutTCK() {
+        Utils.registerTCK(this);
+    }
+    
+    /** Implement to create new context for the test. 
+     * Use {@link Builder} to set context for your technology up.
+     * @return the final context for the test
+     */
+    public abstract BrwsrCtx createContext();
+    
+    /** Create a JSON object as seen by the technology
+     * @param values mapping from names to values of properties
+     * @return the JSON object with filled in values
+     */
+    public abstract Object createJSON(Map<String,Object> values);
+
+    /** Executes script in the context of current window
+     * 
+     * @param script the JavaScript code to execute
+     * @param arguments arguments sent to the script (can be referenced as <code>arguments[0]</code>)
+     * @return the output of the execution
+     */
+    public abstract Object executeScript(String script, Object[] arguments);
+
+    /** Creates a URL which later returns content with given
+     * <code>mimeType</code> and <code>content</code>. The 
+     * content may be processed by the provided <code>parameters</code>.
+     * 
+     * @param content what should be available on the URL. Can contain <code>$0</code>
+     *   <code>$1</code> to reference <code>parameters</code> by their position
+     * @param mimeType the type of the resource
+     * @param parameters names of parameters as reference by <code>content</code>
+     * @return URI the test can connect to to obtain the (processed) content
+     */
+    public abstract URI prepareURL(String content, String mimeType, String[] parameters);
+    
+    /** Gives you list of classes included in the TCK. Their test methods
+     * are annotated by {@link KOTest} annotation. The methods are public
+     * instance methods that take no arguments.
+     * 
+     * @return classes with methods annotated by {@link KOTest} annotation
+     */
+    protected static Class<?>[] testClasses() {
+        return new Class[] { 
+            ConvertTypesTest.class,
+            JSONTest.class,
+            KnockoutTest.class,
+            MinesTest.class,
+            OperationsTest.class,
+            WebSocketTest.class,
+            GCKnockoutTest.class
+        };
+    }
+
+    /** Some implementations cannot fully support web sockets and fail.
+     * 
+     * @return true, if UnsupportedOperationException reported from a web
+     *    socket open operation is acceptable reply
+     */
+    public boolean canFailWebSocketTest() {
+        return false;
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json-tck/src/main/resources/net/java/html/js/tests/global.js
----------------------------------------------------------------------
diff --git a/json-tck/src/main/resources/net/java/html/js/tests/global.js b/json-tck/src/main/resources/net/java/html/js/tests/global.js
new file mode 100644
index 0000000..d2e81a5
--- /dev/null
+++ b/json-tck/src/main/resources/net/java/html/js/tests/global.js
@@ -0,0 +1,44 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+var globalString = 'HTML/Java';
+

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json-tck/src/main/resources/net/java/html/js/tests/global2.js
----------------------------------------------------------------------
diff --git a/json-tck/src/main/resources/net/java/html/js/tests/global2.js b/json-tck/src/main/resources/net/java/html/js/tests/global2.js
new file mode 100644
index 0000000..5881c76
--- /dev/null
+++ b/json-tck/src/main/resources/net/java/html/js/tests/global2.js
@@ -0,0 +1,44 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+var global2String = 'NetBeans';
+

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json-tck/src/main/resources/org/netbeans/html/json/tck/package.html
----------------------------------------------------------------------
diff --git a/json-tck/src/main/resources/org/netbeans/html/json/tck/package.html b/json-tck/src/main/resources/org/netbeans/html/json/tck/package.html
new file mode 100644
index 0000000..4e1eb4a
--- /dev/null
+++ b/json-tck/src/main/resources/org/netbeans/html/json/tck/package.html
@@ -0,0 +1,56 @@
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<!DOCTYPE html>
+<html>
+    <head>
+        <title></title>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    </head>
+    <body>
+        <div>Entry point to the 
+            <a href="KnockoutTCK.html">test compatibility kit</a>.
+        </div>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json-tck/src/test/java/net/java/html/js/tests/BodiesTest.java
----------------------------------------------------------------------
diff --git a/json-tck/src/test/java/net/java/html/js/tests/BodiesTest.java b/json-tck/src/test/java/net/java/html/js/tests/BodiesTest.java
new file mode 100644
index 0000000..0223e41
--- /dev/null
+++ b/json-tck/src/test/java/net/java/html/js/tests/BodiesTest.java
@@ -0,0 +1,83 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.js.tests;
+
+import java.io.InputStream;
+import static org.testng.Assert.*;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class BodiesTest {
+    
+    public BodiesTest() {
+    }
+
+    @Test public void annotationIsStillPresent() throws Exception {
+        InputStream is = Bodies.class.getResourceAsStream("Bodies.class");
+        assertNotNull(is, "Class Stream found");
+        
+        byte[] arr = new byte[is.available()];
+        int len = is.read(arr);
+        
+        assertEquals(len, arr.length, "Fully read");
+        
+        String bytes = new String(arr, "UTF-8");
+        
+        {
+            int idx = bytes.indexOf("Lnet/java/html/js/JavaScriptBody");
+            if (idx == -1) {
+                fail("Expecting JavaScriptBody reference in: " + bytes);
+            }
+        }
+        {
+            int idx = bytes.indexOf("return a + b");
+            if (idx == -1) {
+                fail("Expecting 'return a + b' in the class file: " + bytes);
+            }
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/pom.xml
----------------------------------------------------------------------
diff --git a/json/pom.xml b/json/pom.xml
new file mode 100644
index 0000000..77c95f4
--- /dev/null
+++ b/json/pom.xml
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2016 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<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">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.netbeans.html</groupId>
+    <artifactId>pom</artifactId>
+    <version>2.0-SNAPSHOT</version>
+  </parent>
+  <groupId>org.netbeans.html</groupId>
+  <artifactId>net.java.html.json</artifactId>
+  <version>2.0-SNAPSHOT</version>
+  <packaging>bundle</packaging>
+  <name>JSON Model in Java</name>
+  <url>http://maven.apache.org</url>
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <publicPackages>net.java.html.json,org.netbeans.html.json.spi</publicPackages>
+  </properties>
+  <build>
+      <plugins>
+          <plugin>
+              <groupId>org.apache.felix</groupId>
+              <artifactId>maven-bundle-plugin</artifactId>
+          </plugin>
+          <plugin>
+              <groupId>org.apache.maven.plugins</groupId>
+              <artifactId>maven-source-plugin</artifactId>
+              <version>2.2.1</version>
+              <executions>
+                  <execution>
+                      <id>prepare-sources</id>
+                      <goals>
+                          <goal>jar</goal>
+                      </goals>
+                      <phase>package</phase>
+                  </execution>
+              </executions>
+          </plugin>
+          <plugin>
+              <groupId>org.apache.maven.plugins</groupId>
+              <artifactId>maven-javadoc-plugin</artifactId>
+              <configuration>
+                  <subpackages>net.java.html.json</subpackages>
+                  <skip>false</skip>
+              </configuration>
+          </plugin>
+      </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>org.testng</groupId>
+      <artifactId>testng</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.netbeans.api</groupId>
+      <artifactId>org-openide-util-lookup</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>net.java.html</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+    <description>API for smooth representation of JSON objects in Java. Write your
+application in Java and
+present it using modern HTML rendering technologies like 
+Knockout.
+</description>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/main/java/net/java/html/json/ComputedProperty.java
----------------------------------------------------------------------
diff --git a/json/src/main/java/net/java/html/json/ComputedProperty.java b/json/src/main/java/net/java/html/json/ComputedProperty.java
new file mode 100644
index 0000000..92917db
--- /dev/null
+++ b/json/src/main/java/net/java/html/json/ComputedProperty.java
@@ -0,0 +1,105 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** Can be used in classes annotated with {@link Model} annotation to
+ * define a derived property. Value of derived property is based on values
+ * of {@link Property} as enumerated by {@link Model#properties()}.
+ * <p>
+ * The name of the derived property is the name of the method. The arguments
+ * of the method must match names and types of some of the properties 
+ * from {@link Model#properties()} list. As soon as one of these properties
+ * changes, the method is called to recompute its new value. 
+ * This applies to inner changes in derived properties as well - e.g.
+ * if the dependant property is another type generated by {@link Model @Model} annotation -
+ * changes in its own properties trigger recomputation of this derived 
+ * property as well (since version 0.9).
+ * <p>
+ * Method's return type defines the type of the derived property. It may be
+ * any primitive type, {@link String}, {@link Enum enum type} or a 
+ * type generated by {@link Model @Model} annotation. One may 
+ * also return an array by returning a list of such (boxed) type
+ * (for example {@link java.util.List List}&lt;{@link String}&gt; or {@link java.util.List List}&lt;{@link Integer}&gt;).
+ * <p>
+ * An example testing <a target="_blank" href="http://dew.apidesign.org/dew/#7545568">
+ * whether a number is a prime</a> using a {@link ComputedProperty} is available
+ * on-line.
+ *
+ * @author Jaroslav Tulach
+ */
+@Retention(RetentionPolicy.SOURCE)
+@Target(ElementType.METHOD)
+public @interface ComputedProperty {
+    /** Name of a method to handle changes to the computed property.
+     * By default the computed properties are read-only, however one can
+     * make them mutable by defining a static method that takes
+     * two parameters:
+     * <ol>
+     * <li>the model class</li>
+     * <li>the value - either exactly the return the method annotated
+     *   by this property or a superclass (like {@link Object})</li>
+     * </ol>
+     * Sample code snippet using the <b>write</b> feature of {@link ComputedProperty}
+     * could look like this (assuming the {@link Model model class} named
+     * <em>DataModel</em> has <b>int</b> property <em>value</em>):
+     * <pre>
+     * {@link ComputedProperty @ComputedProperty}(write="setPowerValue")
+     * <b>static int</b> powerValue(<b>int</b> value) {
+     *   <b>return</b> value * value;
+     * }
+     * <b>static void</b> setPowerValue(DataModel m, <b>int</b> value) {
+     *   m.setValue((<b>int</b>){@link Math}.sqrt(value));
+     * }
+     * </pre>
+     * 
+     * @return the name of a method to handle changes to the computed
+     *   property
+     * @since 1.2
+     */
+    public String write() default "";
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/main/java/net/java/html/json/FakeModel.java
----------------------------------------------------------------------
diff --git a/json/src/main/java/net/java/html/json/FakeModel.java b/json/src/main/java/net/java/html/json/FakeModel.java
new file mode 100644
index 0000000..468a15f
--- /dev/null
+++ b/json/src/main/java/net/java/html/json/FakeModel.java
@@ -0,0 +1,122 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json;
+
+/**
+ * Generated for {@link Models}
+ */
+final class FakeModel implements Cloneable {
+    private static Class<Models> modelFor() {
+        return Models.class;
+    }
+    private static final Html4JavaType TYPE = new Html4JavaType();
+    private final org.netbeans.html.json.spi.Proto proto;
+
+    private FakeModel(net.java.html.BrwsrCtx context) {
+        this.proto = TYPE.createProto(this, context);
+    }
+
+    private FakeModel() {
+        this(net.java.html.BrwsrCtx.findDefault(FakeModel.class));
+    }
+
+    public static Object create() {
+        return new FakeModel();
+    }
+
+    private static class Html4JavaType extends org.netbeans.html.json.spi.Proto.Type<FakeModel> {
+
+        private Html4JavaType() {
+            super(FakeModel.class, Models.class, 0, 0);
+        }
+
+        @Override
+        public void setValue(FakeModel data, int type, Object value) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Object getValue(FakeModel data, int type) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void call(FakeModel model, int type, Object data, Object ev) throws Exception {
+            switch (type) {
+            }
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public org.netbeans.html.json.spi.Proto protoFor(Object obj) {
+            return ((FakeModel) obj).proto;
+        }
+
+        @Override
+        public void onChange(FakeModel model, int type) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void onMessage(FakeModel model, int index, int type, Object data, Object[] params) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public FakeModel read(net.java.html.BrwsrCtx c, Object json) {
+            return new FakeModel(c, json);
+        }
+
+        @Override
+        public FakeModel cloneTo(FakeModel o, net.java.html.BrwsrCtx c) {
+            return o;
+        }
+    }
+
+    private FakeModel(net.java.html.BrwsrCtx c, Object json) {
+        this(c);
+        Object[] ret = new Object[0];
+        proto.extract(json, new String[]{}, ret);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/main/java/net/java/html/json/Function.java
----------------------------------------------------------------------
diff --git a/json/src/main/java/net/java/html/json/Function.java b/json/src/main/java/net/java/html/json/Function.java
new file mode 100644
index 0000000..9babc31
--- /dev/null
+++ b/json/src/main/java/net/java/html/json/Function.java
@@ -0,0 +1,122 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json;
+
+import java.io.PrintStream;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.netbeans.html.json.spi.FunctionBinding;
+
+/** Methods in class annotated by {@link Model} can be 
+ * annotated by this annotation to signal that they should be available
+ * as functions to users of the model classes. The method
+ * should be non-private, static (unless {@link Model#instance() instance mode} is on)
+ * and return <code>void</code>.
+ * It may take few arguments. The first argument can be the type of
+ * the associated model class, the other argument can be of any type,
+ * but has to be named <code>data</code> - this one represents the
+ * actual data the function was invoked on. Example:
+ * <pre>
+ * 
+ * {@link Model @Model}(className="Names", properties={
+ *   {@link Property @Property}(name = "selectedName", type=String.class),
+ *   {@link Property @Property}(name = "names", type=String.class, array = true)
+ * })
+ * static class NamesModel {
+ *   {@link Function @Function} static void <b>nameSelected</b>(Names myModel, String data) {
+ *     myModel.setSelectedName(data);
+ *   }
+ * 
+ *   static void initialize() {
+ *     Names pageModel = new Names("---", "Jarda", "Pepa", "Honza", "Jirka", "Tomáš");
+ *     pageModel.applyBindings();
+ *   }
+ * }
+ * 
+ * // associated <a target="_blank" href="http://knockoutjs.com/">Knockout</a> HTML page:
+ * 
+ * Selected name: &lt;span data-bind="text: selectedName"&gt;&lt;/span&gt;
+ * &lt;ul data-bind="foreach: names"&gt;
+ *   &lt;li&gt;
+ *     &lt;a data-bind="text: $data, click: $root.nameSelected" href="#"&gt;&lt;/a&gt;
+ *   &lt;/li&gt;
+ * &lt;/ul&gt; 
+ * </pre>
+ * The above example would render:
+ * <hr>
+ * Selected name: <span>---</span>
+ * <ul>
+ *   <li>Jarda</li>
+ *   <li>Pepa</li>
+ *   <li>Honza</li>
+ *   <li>Jirka</li>
+ *   <li>Tomáš</li>
+ * </ul>
+ * <hr>
+ * and after clicking on one of the names the <code>---</code> would be replaced
+ * by selected name. 
+ * Try <a target="_blank" href="http://dew.apidesign.org/dew/#8848505">this sample on-line</a>!
+ * <p>
+ * There can be additional arguments in the method which can extract information
+ * from (typically event object) sent as a second parameter of the function
+ * {@link FunctionBinding#call(java.lang.Object, java.lang.Object) dispatch method}.
+ * Such arguments can be of primitive types (<code>int</code>, <code>double</code>
+ * or {@link String}). Their names are used to extract values of appropriate
+ * properties from the event object. The following function...
+ * <pre>
+ * {@link Function @Function} static void <b>meaningOfWorld</b>(Names myModel, String data, int answer) {
+ *   {@link System}.out.{@link PrintStream#println(int) println}(answer);
+ * }
+ * // would print <b>42</b> if the dispatch method:
+ * {@link FunctionBinding#call(java.lang.Object, java.lang.Object) meaningOfWorld.call}(model, json)
+ * </pre>
+ * is called with equivalent of <code>var json = { 'answer' : 42 }</code>.
+ * 
+ * @author Jaroslav Tulach
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.SOURCE)
+public @interface Function {
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/main/java/net/java/html/json/Model.java
----------------------------------------------------------------------
diff --git a/json/src/main/java/net/java/html/json/Model.java b/json/src/main/java/net/java/html/json/Model.java
new file mode 100644
index 0000000..7d467ab
--- /dev/null
+++ b/json/src/main/java/net/java/html/json/Model.java
@@ -0,0 +1,356 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.net.URL;
+import java.util.List;
+
+/** Defines a model class that represents a single 
+ * <a target="_blank" href="http://en.wikipedia.org/wiki/JSON">JSON</a>-like object
+ * named {@link #className()}. The generated class contains
+ * getters and setters for properties defined via {@link #properties()} and
+ * getters for other, derived properties defined by annotating methods
+ * of this class by {@link ComputedProperty}. Each property
+ * can be of primitive type, an {@link Enum enum type} or (in order to create 
+ * nested <a target="_blank" href="http://en.wikipedia.org/wiki/JSON">JSON</a> structure)
+ * of another {@link Model class generated by @Model} annotation. Each property
+ * can either represent a single value or be an array of its values.
+ * <p>
+ * The {@link #className() generated class}'s <code>toString</code> method
+ * converts the state of the object into 
+ * <a target="_blank" href="http://en.wikipedia.org/wiki/JSON">JSON</a> format. One can
+ * use {@link Models#parse(net.java.html.BrwsrCtx, java.lang.Class, java.io.InputStream)}
+ * method to read the JSON text stored in a file or other stream back into the Java model. 
+ * One can also use {@link OnReceive @OnReceive} annotation to read the model
+ * asynchronously from a {@link URL}.
+ * <p>
+ * An example where one defines class <code>Person</code> with four
+ * properties (<code>firstName</code>, <code>lastName</code>, array of <code>addresses</code> and
+ * <code>fullName</code>) follows:
+ * <pre>
+ * {@link Model @Model}(className="Person", properties={
+ *   {@link Property @Property}(name = "firstName", type=String.<b>class</b>),
+ *   {@link Property @Property}(name = "lastName", type=String.<b>class</b>)
+ *   {@link Property @Property}(name = "addresses", type=Address.<b>class</b>, array = <b>true</b>)
+ * })
+ * <b>static class</b> PersonModel {
+ *   {@link ComputedProperty @ComputedProperty}
+ *   <b>static</b> String fullName(String firstName, String lastName) {
+ *     <b>return</b> firstName + " " + lastName;
+ *   }
+ * 
+ *   {@link ComputedProperty @ComputedProperty}
+ *   <b>static</b> String mainAddress({@link List List&lt;Address&gt;} addresses) {
+ *     <b>for</b> (Address a : addresses) {
+ *       <b>return</b> a.getStreet() + " " + a.getTown();
+ *     }
+ *     <b>return</b> "No address";
+ *   }
+ * 
+ *   {@link Model @Model}(className="Address", properties={
+ *     {@link Property @Property}(name = "street", type=String.<b>class</b>),
+ *     {@link Property @Property}(name = "town", type=String.<b>class</b>)
+ *   })
+ *   <b>static class</b> AddressModel {
+ *   }
+ * }
+ * </pre>
+ * The generated model class has a default constructor, and also <em>quick
+ * instantiation</em> constructor that accepts all non-array properties 
+ * (in the order used in the {@link #properties()} attribute) and vararg list
+ * for the first array property (if any). One can thus use following code
+ * to create an instance of the Person and Address classes:
+ * <pre>
+ * Person p = <b>new</b> Person("Jaroslav", "Tulach",
+ *   <b>new</b> Address("Markoušovice", "Úpice"),
+ *   <b>new</b> Address("V Parku", "Praha")
+ * );
+ * // p.toString() then returns equivalent of following <a target="_blank" href="http://en.wikipedia.org/wiki/JSON">JSON</a> object
+ * {
+ *   "firstName" : "Jaroslav",
+ *   "lastName" : "Tulach",
+ *   "addresses" : [
+ *     { "street" : "Markoušovice", "town" : "Úpice" },
+ *     { "street" : "V Parku", "town" : "Praha" },
+ *   ]
+ * }
+ * </pre>
+ * <p>
+ * In case you are using <a target="_blank" href="http://knockoutjs.com/">Knockout technology</a>
+ * for Java then you can associate such model object with surrounding HTML page by
+ * calling: <code>p.applyBindings();</code> (in case you specify {@link #targetId()}. 
+ * The page can then use regular
+ * <a target="_blank" href="http://knockoutjs.com/">Knockout</a> bindings to reference your
+ * model and create dynamic connection between your model in Java and 
+ * live DOM structure in the browser:
+ * </p>
+ * <pre>
+ * Name: &lt;span data-bind="text: fullName"&gt;
+ * &lt;div data-bind="foreach: addresses"&gt;
+ *   Lives in &lt;span data-bind="text: town"/&gt;
+ * &lt;/div&gt;
+ * </pre>
+ * 
+ * <h3>Access Raw <a target="_blank" href="http://knockoutjs.com/">Knockout</a> Observables</h3>
+ * 
+ * One can obtain <em>raw</em> JavaScript object representing the 
+ * instance of {@link Model model class} (with appropriate
+ * <a target="_blank" href="http://knockoutjs.com/">Knockout</a> <b>observable</b> properties)
+ * by calling {@link Models#toRaw(java.lang.Object) Models.toRaw(p)}. For 
+ * example here is a way to obtain the value of <code>fullName</code> property
+ * (inefficient as it switches between Java and JavaScript back and forth, 
+ * but functional and instructive) via a JavaScript call:
+ * <pre>
+ * {@link net.java.html.js.JavaScriptBody @JavaScriptBody}(args = "raw", javacall = true, body =
+ *   "return raw.fullName();" // yes, the <a target="_blank" href="http://knockoutjs.com/">Knockout</a> property is a function
+ * )
+ * static native String jsFullName(Object raw);
+ * // and later
+ * Person p = ...;
+ * String fullName = jsFullName({@link Models#toRaw(java.lang.Object) Models.toRaw(p)});
+ * </pre>
+ * The above shows how to read a value from <a target="_blank" href="http://knockoutjs.com/">Knockout</a>
+ * observable. There is a way to change the value too:
+ * One can pass a parameter to the property-function and then
+ * it acts like a setter (of course not in the case of read only <code>fullName</code> property,
+ * but for <code>firstName</code> or <code>lastName</code> the setter is
+ * available). Everything mentioned in this paragraph applies only when 
+ * <a target="_blank" href="http://knockoutjs.com/">Knockout</a> technology is active
+ * other technologies may behave differently.
+ * 
+ * <h4>Copy to Plain JSON</h4>
+ * There is a way to pass a value of a Java {@link Model model class} instance 
+ * by copy and convert 
+ * the {@link Model the whole object} into plain 
+ * <a target="_blank" href="http://en.wikipedia.org/wiki/JSON">JSON</a>. Just
+ * print it as a string and parse it in JavaScript:
+ * <pre>
+ * {@link net.java.html.js.JavaScriptBody @JavaScriptBody}(args = { "txt" }, body =
+ *   "return JSON.parse(txt);"
+ * )
+ * private static native Object parseJSON(String txt);
+ * 
+ * public static Object toPlainJSON(Object model) {
+ *   return parseJSON(model.toString());
+ * }
+ * </pre>
+ * The newly returned instance is a one time copy of the original model and is no longer
+ * connected to it. The copy based behavior is independent on any 
+ * particular technology and should work
+ * in <a target="_blank" href="http://knockoutjs.com/">Knockout</a> as well as other
+ * technology implementations.
+ * 
+ * <h4>References</h4>
+ * 
+ * Visit an <a target="_blank" href="http://dew.apidesign.org/dew/#7510833">on-line demo</a>
+ * to see a histogram driven by the {@link Model} annotation or try 
+ * a little <a target="_blank" href="http://dew.apidesign.org/dew/#7263102">math test</a>.
+ *
+ * @author Jaroslav Tulach
+ */
+@Retention(RetentionPolicy.SOURCE)
+@Target(ElementType.TYPE)
+public @interface Model {
+    /** Name of the model class.
+     * @return valid Java identifier to use as a name of the model class
+     */
+    String className();
+    /** List of properties in the model.
+     * @return array of property definitions
+     */
+    Property[] properties();
+    
+    /** The id of an element to bind this model too. If this
+     * property is specified an <code>applyBindings</code> method
+     * in the model class is going to be generated which later calls
+     * {@link Models#applyBindings(java.lang.Object, java.lang.String)}
+     * with appropriate <code>targetId</code>. If the <code>targetId</code>
+     * is specified as empty string, <code>null</code> value is passed
+     * to {@link Models#applyBindings(java.lang.Object, java.lang.String)} method.
+     * If the <code>targetId</code> is not specified at all, no public
+     * <code>applyBindings</code> method is generated at all (a change compared
+     * to previous versions of this API).
+     * 
+     * @return an empty string (means apply globally), or ID of a (usually DOM)
+     *    element to apply this model after calling its generated
+     *    <code>applyBindings()</code> method to
+     * @since 1.1
+     */
+    String targetId() default "";
+
+    /** Controls whether builder-like setters shall be generated. Once this
+     * attribute is set, all {@link #properties()} will get a builder like
+     * setter (takes value of the property and returns <code>this</code>
+     * so invocations can be chained). When this attribute is specified,
+     * the non-default constructor isn't generated at all.
+     * <p>
+     * Specifying <code>builder="assign"</code>
+     * and having {@link #properties() properties} <code>name</code> and
+     * <code>age</code> will generate method: <pre>
+     * <b>public</b> MyModel assignName(String name) { ... }
+     * <b>public</b> MyModel assignAge(int age) { ... }
+     * </pre>
+     * These methods can then be chained as <pre>
+     * MyModel m = <b>new</b> MyModel().assignName("name").assignAge(3);
+     * </pre>
+     * The <code>builder</code> attribute can be set to empty string <code>""</code> -
+     * then it is possible that some property names clash with Java keywords.
+     * It's responsibility of the user to specify valid builder prefix,
+     * so the generated methods are compilable.
+     *
+     * @return the prefix to put before {@link Property property} names when
+     *   generating their builder methods
+     * @since 1.3
+     */
+    String builder() default "";
+    
+    /** Controls keeping of per-instance private state. Sometimes
+     * the class generated by the {@link Model @Model annotation} needs to
+     * keep non-public, or non-JSON like state. One can achieve that by
+     * specifying <code>instance=true</code> when using the annotation. Then
+     * the generated class gets a pointer to the instance of the annotated
+     * class (there needs to be default constructor) and all the {@link ModelOperation @ModelOperation},
+     * {@link Function @Function}, {@link OnPropertyChange @OnPropertyChange}
+     * and {@link OnReceive @OnReceive} methods may be non-static. The
+     * instance of the implementation class isn't accessible directly, just
+     * through calls to above defined (non-static) methods. Example:
+     * <pre>
+     * {@link Model @Model}(className="Data", instance=<b>true</b>, properties={
+     *   {@link Property @Property}(name="message", type=String.<b>class</b>)
+     * })
+     * <b>final class</b> DataPrivate {
+     *   <b>private int</b> count;
+     * 
+     *   {@link ModelOperation @ModelOperation} <b>void</b> increment(Data model) {
+     *     count++;
+     *   }
+     * 
+     *   {@link ModelOperation @ModelOperation} <b>void</b> hello(Data model) {
+     *     model.setMessage("Hello " + count + " times!");
+     *   }
+     * }
+     * Data data = <b>new</b> Data();
+     * data.increment();
+     * data.increment();
+     * data.increment();
+     * data.hello();
+     * <b>assert</b> data.getMessage().equals("Hello 3 times!");
+     * </pre>
+     * <p>
+     * The methods annotated by {@link ComputedProperty} need to remain static, as 
+     * they are supposed to be <em>pure</em> functions (e.g. depend only on their parameters)
+     * and shouldn't use any internal state.
+     * </p>
+     * <p><b>How do I initialize private values?</b>
+     * The implementation class (the one annotated by {@link Model @Model} annotation) 
+     * needs to have accessible default constructor. That constructor is used to 
+     * create the instance. Obviously such constructor does not have 
+     * any parameters, so no initialization is possible.
+     * </p>
+     * <p>
+     * Later one can, however, call any {@link ModelOperation @ModelOperation}
+     * method and pass in additional configuration parameters. In the above 
+     * example it should be possible add
+     * </p>
+     * <pre>
+     * {@link ModelOperation @ModelOperation} <b>void</b> init(Data model, int count) {
+     *   <b>this</b>.count = count;
+     * }
+     * </pre><p>
+     * and then one can initialize the model using the <code>init</code> as:
+     * </p>
+     * <pre>
+     * Data data = <b>new</b> Data();
+     * data.init(2);
+     * data.increment();
+     * data.hello();
+     * <b>assert</b> data.getMessage().equals("Hello 3 times!");
+     * </pre><p>
+     * Why there has to be default constructor? Because instances of 
+     * classes generated by {@link Model @Model annotation} may be constructed
+     * by the system as 
+     * {@link Models#fromRaw(net.java.html.BrwsrCtx, java.lang.Class, java.lang.Object) wrappers around existing JavaScript objects}
+     * - then
+     * there is nobody to provide additional parameters at construction time.
+     * </p>
+     * <p><b>How do I read private values?</b>
+     * The methods annotated by {@link ModelOperation} must return <code>void</code>
+     * (as they can run asynchronously) and as such they aren't suitable for
+     * returning values back to the caller. In case something like that is
+     * needed, one can use the approach of the <code>hello</code> method - e.g.
+     * set value of some {@link Property property} that has a getter:
+     * </p>
+     * <pre>
+     * data.hello();
+     * <b>assert</b> data.getMessage().equals("Hello 3 times!");
+     * </pre><p>
+     * Or one can use actor-like callbacks. Define callback interface and
+     * use it in a {@link ModelOperation @ModelOperation} method:
+     * </p>
+     * <pre>
+     * <b>public interface</b> ReadCount {
+     *   <b>public void</b> notifyCount(int count);
+     * }
+     * {@link ModelOperation @ModelOperation} <b>void</b> readCount(Data model, ReadCount callback) {
+     *   callback.readCount(<b>this</b>.count);
+     * }
+     * Data data = <b>new</b> Data();
+     * data.init(2);
+     * data.increment();
+     * data.readCount(count -&gt; System.out.println("count should be 3: " + count));
+     * </pre><p>
+     * The provided lambda-function callback may be invoked immediately 
+     * or asynchronously as documentation for {@link ModelOperation} 
+     * annotation describes.
+     * </p>
+     * 
+     * @return <code>true</code> if the model class should keep pointer to
+     *   instance of the implementation class
+     * @since 1.3
+     */
+    boolean instance() default false;
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/main/java/net/java/html/json/ModelOperation.java
----------------------------------------------------------------------
diff --git a/json/src/main/java/net/java/html/json/ModelOperation.java b/json/src/main/java/net/java/html/json/ModelOperation.java
new file mode 100644
index 0000000..5f8ea12
--- /dev/null
+++ b/json/src/main/java/net/java/html/json/ModelOperation.java
@@ -0,0 +1,100 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** The threading model of classes generated by {@link Model @Model} requires
+ * that all operations are perform from the originating thread - unless they
+ * are invoked as {@link ModelOperation @ModelOperation} methods.
+ * <p>
+ * A method in a class annotated by {@link Model @Model} annotation may be
+ * annotated by {@link ModelOperation @ModelOperation}. The method has
+ * to be static (unless {@link Model#instance() instance mode} is on), 
+ * non-private and return <code>void</code>. The first parameter
+ * of the method must be the {@link Model#className() model class} followed
+ * by any number of additional arguments.
+ * <p>
+ * As a result method of the same name and the same list of additional arguments
+ * (e.g. without the first model class one) will be generated into the 
+ * {@link Model#className() model class}. This method can be invoked on any
+ * thread, any time and it is the responsibility of model manipulating
+ * technology to ensure the model is available and only then call back to 
+ * the original method annotated by {@link ModelOperation @ModelOperation}.
+ * The call may happen synchronously (if possible), or be delayed and invoked
+ * later (on appropriate dispatch thread), without blocking the caller.
+ * <pre>
+ * 
+ * {@link Model @Model}(className="Names", properties={
+ *   {@link Property @Property}(name = "names", type=String.class, array = true)
+ * })
+ * static class NamesModel {
+ *   {@link ModelOperation @ModelOperation} static void <b>updateNames</b>(Names model, {@link java.util.List}&lt;String;gt; arr) {
+ *     <em>// can safely access the model</em>
+ *     model.getNames().addAll(arr);
+ *   }
+ * 
+ *   static void initialize() {
+ *     final Names pageModel = new Names();
+ *     pageModel.applyBindings();
+ * 
+ *     <em>// spawn to different thread</em>
+ *     {@link java.util.concurrent.Executors}.newSingleThreadExecutor().execute({
+ *       List&lt;String&gt; arr = <em>// ... obtain the names somewhere from network</em>
+ *       pageModel.<b>updateNames</b>(arr);
+ *       // returns immediately, later it invokes the model operation
+ *     });
+ * 
+ *   }
+ * }
+ * </pre>
+ * 
+ * @author Jaroslav Tulach
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.SOURCE)
+public @interface ModelOperation {
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/main/java/net/java/html/json/Models.java
----------------------------------------------------------------------
diff --git a/json/src/main/java/net/java/html/json/Models.java b/json/src/main/java/net/java/html/json/Models.java
new file mode 100644
index 0000000..71dbb39
--- /dev/null
+++ b/json/src/main/java/net/java/html/json/Models.java
@@ -0,0 +1,191 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json;
+
+import net.java.html.BrwsrCtx;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collection;
+import org.netbeans.html.json.impl.JSON;
+import org.netbeans.html.json.spi.Technology;
+
+/** Information about and 
+ * operations for classes generated by the {@link Model @Model}
+ * annotation.
+ *
+ * @author Jaroslav Tulach
+ */
+public final class Models {
+    private Models() {
+    }
+   
+    /** Finds out whether given class is a model class - e.g. has been
+     * generated by {@link Model @Model} annotation.
+     * 
+     * @param clazz the class to test
+     * @return true, if <code>clazz</code> was generated by {@link Model} annotation
+     * @since 0.2
+     */
+    public static boolean isModel(Class<?> clazz) {
+        return JSON.isModel(clazz);
+    }
+    
+    /** Binds given model to another context. 
+     * 
+     * @param <Model> class defined by {@link net.java.html.json.Model} annotation
+     * @param model instance of a model defined by {@link net.java.html.json.Model} annotation
+     * @param context context to which the model should be bound
+     * @return new instance of model bound to new <code>context</code>
+     * @throws IllegalArgumentException in case the instance is not generated by model interface
+     * @since 0.4
+     */
+    public static <Model> Model bind(Model model, BrwsrCtx context) {
+        return JSON.bindTo(model, context);
+    }
+    
+    /** Generic method to parse content of a model class from a stream.
+     * 
+     * @param <M> type of the <code>model</code> class
+     * @param c context of the technology to use for reading 
+     * @param model the model class generated by {@link Model} annotation
+     * @param is input stream with data
+     * @return new instance of the model class
+     * @throws java.io.IOException throw when reading from <code>is</code> faces problems
+     * @since 0.2
+     */
+    public static <M> M parse(BrwsrCtx c, Class<M> model, InputStream is) throws IOException {
+        return JSON.readStream(c, model, is, null);
+    }
+    
+    /** Generic method to parse stream, that can possibly contain array
+     * of specified objects.
+     * 
+     * @param <M> the type of the individal JSON object
+     * @param c context of the technology to use for reading 
+     * @param model the model class generated by {@link Model} annotation
+     * @param is input stream with data
+     * @param collectTo collection to add the individual model instances to.
+     *   If the stream contains an object, one instance will be added, if
+     *   it contains an array, the number of array items will be added to
+     *   the collection
+     * @throws IOException thrown when an I/O problem appears
+     * @since 0.8.3
+     */
+    public static <M> void parse(
+        BrwsrCtx c, Class<M> model, 
+        InputStream is, Collection<? super M> collectTo
+    ) throws IOException {
+        collectTo.getClass();
+        JSON.readStream(c, model, is, collectTo);
+    }
+    
+    /** Converts an existing, raw, JSON object into a {@link Model model class}.
+     * 
+     * @param <M> the type of the model class
+     * @param ctx context of the technology to use for converting
+     * @param model the model class
+     * @param jsonObject original instance of the JSON object
+     * @return new instance of the model class
+     * @since 0.7
+     */
+    public static <M> M fromRaw(BrwsrCtx ctx, Class<M> model, Object jsonObject) {
+        M value = JSON.read(ctx, model, jsonObject);
+        JSON.readBindings(ctx, value, jsonObject);
+        return value;
+    }
+    
+    /** Converts an existing {@link Model model} into its associated, raw 
+     * JSON object. The object may, but does not have to, be the same instance
+     * as the model object.
+     * 
+     * @param model the model object
+     *     (can be <code>null</code> to initialize the associated {@link Technology})
+     * @return the raw JSON object associated with the model
+     *     (<code>null</code> if the <code>model</code> parameter was null)
+     * @throws IllegalArgumentException if the <code>model</code> is 
+     *    not instance of a class
+     *    generated by {@link Model model annotation} processor.
+     * @since 0.7
+     */
+    public static Object toRaw(Object model) {
+        if (model == null) {
+            toRaw(FakeModel.create());
+            return null;
+        }
+        final Class<? extends Object> type = model.getClass();
+        if (!isModel(type)) {
+            throw new IllegalStateException("Not a model " + type);
+        }
+        return JSON.find(model);
+    }
+    
+    /** Apply bindings of a model class to overall page. In <em>ko4j</em> mode,
+     * it binds the model values to the currently active page. 
+     * 
+     * @param model instance of a {@link Model class}
+     * @throws IllegalArgumentException if the <code>model</code> is not
+     * instance of a class generated by {@link Model model annotation}
+     * processor.
+     * 
+     * @since 0.7
+     */
+    public static void applyBindings(Object model) {
+        JSON.applyBindings(model, null);
+    }
+    
+    
+    /** Apply bindings of a model class. In <em>ko4j</em> mode,
+     * it binds the model values to an element on currently active page.
+     * 
+     * @param model instance of a {@link Model class}
+     * @param targetId the id of the element to apply the binding to 
+     * @throws IllegalArgumentException if the <code>model</code> is not
+     * instance of a class generated by {@link Model model annotation}
+     * processor.
+     * 
+     * @since 1.1
+     */
+    public static void applyBindings(Object model, String targetId) {
+        JSON.applyBindings(model, targetId);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/main/java/net/java/html/json/OnPropertyChange.java
----------------------------------------------------------------------
diff --git a/json/src/main/java/net/java/html/json/OnPropertyChange.java b/json/src/main/java/net/java/html/json/OnPropertyChange.java
new file mode 100644
index 0000000..a5eded0
--- /dev/null
+++ b/json/src/main/java/net/java/html/json/OnPropertyChange.java
@@ -0,0 +1,100 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** Marks a method that is going to be notified when a 
+ * property defined by {@link Model} has been changed. This is
+ * especially useful when one wants to react to changes in the 
+ * model caused by the rendered view. In case of 
+ * <a href="http://knockoutjs.com">knockout.js</a> technology
+ * one could for example react to selection of a name from a combo
+ * box:
+ * <pre>
+ * 
+ * &lt;!-- associates the selected value with property <em>name</em> --&gt;
+ * 
+ * &lt;select data-bind="value: name"&gt;
+ *   &lt;option&gt;Jiří&lt;/option&gt;
+ *   &lt;option&gt;Jarda&lt;/option&gt;
+ *   &lt;option&gt;Petr&lt;/option&gt;
+ *   &lt;option&gt;Tomáš&lt;/option&gt;
+ * &lt;/select&gt;
+ * 
+ * // Java code snippet reacting to change of the <em>name</em> property:
+ * 
+ * {@link OnPropertyChange @OnPropertyChange}("name") 
+ * <b>static void</b> propertyChanged(AModel inst, {@link String} propertyName) {
+ *   // schedule some operation
+ *   // on the model
+ * }
+ * </pre>
+ * The method's first argument should be the instance of the 
+ * associated {@link Model model class}. The method shall be non-private
+ * and unless {@link Model#instance() instance mode} is on also static.
+ * There can be an optional second {@link String} argument which will be set
+ * to the name of the changed property. The second argument is only useful when
+ * a single method reacts to changes in multiple properties.
+ * <p>
+ * An online example using this technique is 
+ * <a target="_blank" href="http://dew.apidesign.org/dew/#7138581">available here</a> -
+ * it observes selection in a combo box and in case it changes 
+ * the example sends a network
+ * request and {@link net.java.html.json.OnReceive asynchronously updates}
+ * list of code snippets.
+ *
+ * @author Jaroslav Tulach
+ */
+@Retention(RetentionPolicy.SOURCE)
+@Target(ElementType.METHOD)
+public @interface OnPropertyChange {
+    /** Name(s) of the properties. One wishes to observe.
+     * 
+     * @return valid java identifier
+     */
+    String[] value();
+}


[07/24] incubator-netbeans-html4j git commit: [INFRA-15006] Initial donation of HTML/Java NetBeans API. Equivalent to the content of html4j-donation-review.zip donated as part of ApacheNetBeansDonation1.zip with SHA256 being 7f2ca0f61953a190613c9a0fbcc1b

Posted by jt...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/net/java/html/json/MapModelTest.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/net/java/html/json/MapModelTest.java b/json/src/test/java/net/java/html/json/MapModelTest.java
new file mode 100644
index 0000000..3a9143d
--- /dev/null
+++ b/json/src/test/java/net/java/html/json/MapModelTest.java
@@ -0,0 +1,521 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json;
+
+import net.java.html.BrwsrCtx;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.ListIterator;
+import java.util.Map;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.json.spi.FunctionBinding;
+import org.netbeans.html.json.spi.JSONCall;
+import org.netbeans.html.json.spi.PropertyBinding;
+import org.netbeans.html.json.spi.Technology;
+import org.netbeans.html.json.spi.Transfer;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class MapModelTest {
+    private MapTechnology t;
+    private BrwsrCtx c;
+
+    @BeforeMethod public void initTechnology() {
+        t = new MapTechnology();
+        c = Contexts.newBuilder().register(Technology.class, t, 1).
+            register(Transfer.class, t, 1).build();
+    }
+    
+    @Test public void isThereNoApplyBinding() throws Exception {
+        try {
+            Person.class.getMethod("applyBindings");
+        } catch (NoSuchMethodException ex) {
+            // OK
+            return;
+        }
+        fail("There should be no applyBindings() method");
+    }
+    
+    @Test public void isThereABinding() throws Exception {
+        Person p = Models.bind(new Person(), c);
+        Models.applyBindings(p);
+        assertNull(t.appliedId, "Applied globally");
+        p.setFirstName("Jarda");
+        
+        Map m = (Map)Models.toRaw(p);
+        Object v = m.get("firstName");
+        assertNotNull(v, "Value should be in the map");
+        assertEquals(v.getClass(), One.class, "It is instance of One");
+        One o = (One)v;
+        assertEquals(o.changes, 1, "One change so far");
+        assertFalse(o.pb.isReadOnly(), "Mutable property");
+        
+        assertEquals(o.get(), "Jarda", "Value should be in the map");
+        
+        o.set("Karle");
+        
+        assertEquals(p.getFirstName(), "Karle", "Model value updated");
+        assertEquals(o.changes, 2, "Snd change");
+    }
+    
+    @Test public void applyLocally() throws Exception {
+        Person p = Models.bind(new Person(), c);
+        Models.applyBindings(p, "local");
+        assertEquals(t.appliedId, "local", "Applied locally");
+    }
+    
+    @Test public void dontNotifySameProperty() throws Exception {
+        Person p = Models.bind(new Person(), c);
+        p.setFirstName("Jirka");
+        
+        Map m = (Map)Models.toRaw(p);
+        Object v = m.get("firstName");
+        assertNotNull(v, "Value should be in the map");
+        assertEquals(v.getClass(), One.class, "It is instance of One");
+        One o = (One)v;
+        assertEquals(o.changes, 0, "No change so far the only one change happened before we connected");
+        
+        p.setFirstName(new String("Jirka"));
+        assertEquals(o.changes, 0, "No change so far, the value is the same");
+        
+        p.setFirstName("Jarda");
+        assertFalse(o.pb.isReadOnly(), "Mutable property");
+        
+        assertEquals(o.get(), "Jarda", "Value should be in the map");
+        
+        o.set("Karle");
+        
+        assertEquals(p.getFirstName(), "Karle", "Model value updated");
+        assertEquals(o.changes, 2, "Snd change");
+    }
+    
+    @Test public void canSetEnumAsString() throws Exception {
+        Person p = Models.bind(new Person(), c);
+        p.setFirstName("Jirka");
+        p.setSex(Sex.MALE);
+        
+        Map m = (Map)Models.toRaw(p);
+        Object v = m.get("sex");
+        assertNotNull(v, "Value should be in the map");
+        assertEquals(v.getClass(), One.class, "It is instance of One");
+        One o = (One)v;
+        
+        o.set("FEMALE");
+
+        assertEquals(p.getSex(), Sex.FEMALE, "Changed to female");
+    }
+    
+    @Test public void derivedProperty() throws Exception {
+        Person p = Models.bind(new Person(), c);
+        
+        Map m = (Map)Models.toRaw(p);
+        Object v = m.get("fullName");
+        assertNotNull(v, "Value should be in the map");
+        assertEquals(v.getClass(), One.class, "It is instance of One");
+        One o = (One)v;
+        assertTrue(o.pb.isReadOnly(), "Mutable property");
+    }
+    
+    @Test public void changeSex() {
+        Person p = Models.bind(new Person(), c);
+        p.setFirstName("Trans");
+        p.setSex(Sex.MALE);
+        
+        Map m = (Map)Models.toRaw(p);
+        Object o = m.get("changeSex");
+        assertNotNull(o, "Function registered in the model");
+        assertEquals(o.getClass(), One.class);
+        
+        One one = (One)o;
+        assertNotNull(one.fb, "Function binding specified");
+        
+        one.fb.call(null, null);
+        
+        assertEquals(p.getSex(), Sex.FEMALE, "Changed");
+    }
+    
+    @Test public void setSex() {
+        Person p = Models.bind(new Person(), c);
+        p.setFirstName("Trans");
+        
+        Map m = (Map)Models.toRaw(p);
+        Object o = m.get("changeSex");
+        assertNotNull(o, "Function registered in the model");
+        assertEquals(o.getClass(), One.class);
+        
+        One one = (One)o;
+        assertNotNull(one.fb, "Function binding specified");
+        
+        one.fb.call("FEMALE", new Object());
+        
+        assertEquals(p.getSex(), Sex.FEMALE, "Changed");
+    }
+
+    @Test public void changeComputedProperty() {
+        Modelik p = Models.bind(new Modelik(), c);
+        p.setValue(5);
+
+        Map m = (Map)Models.toRaw(p);
+        Object o = m.get("powerValue");
+        assertNotNull(o, "Value is there");
+        assertEquals(o.getClass(), One.class);
+
+        One one = (One)o;
+        assertNotNull(one.pb, "Prop binding specified");
+
+        assertEquals(one.pb.getValue(), 25, "Power of 5");
+
+        one.pb.setValue(16);
+        assertEquals(p.getValue(), 4, "Square root of 16");
+    }
+    
+    @Test public void removeViaIterator() {
+        People p = Models.bind(new People(), c);
+        p.getNicknames().add("One");
+        p.getNicknames().add("Two");
+        p.getNicknames().add("Three");
+
+        Map m = (Map)Models.toRaw(p);
+        Object o = m.get("nicknames");
+        assertNotNull(o, "List registered in the model");
+        assertEquals(o.getClass(), One.class);
+        One one = (One)o;
+        
+        
+        assertEquals(one.changes, 0, "No change");
+        
+        Iterator<String> it = p.getNicknames().iterator();
+        assertEquals(it.next(), "One");
+        assertEquals(it.next(), "Two");
+        it.remove();
+        assertEquals(it.next(), "Three");
+        assertFalse(it.hasNext());
+        
+        
+        assertEquals(one.changes, 1, "One change");
+    }
+    
+    @Test public void removeViaListIterator() {
+        People p = Models.bind(new People(), c);
+        p.getNicknames().add("One");
+        p.getNicknames().add("Two");
+        p.getNicknames().add("Three");
+
+        Map m = (Map)Models.toRaw(p);
+        Object o = m.get("nicknames");
+        assertNotNull(o, "List registered in the model");
+        assertEquals(o.getClass(), One.class);
+        One one = (One)o;
+        
+        
+        assertEquals(one.changes, 0, "No change");
+        
+        ListIterator<String> it = p.getNicknames().listIterator(1);
+        assertEquals(it.next(), "Two");
+        it.remove();
+        assertEquals(it.next(), "Three");
+        assertFalse(it.hasNext());
+        
+        
+        assertEquals(one.changes, 1, "One change");
+        
+        it.set("3");
+        assertEquals(p.getNicknames().get(1), "3");
+        
+        assertEquals(one.changes, 2, "Snd change");
+    }
+
+    @Test public void subListChange() {
+        People p = Models.bind(new People(), c);
+        p.getNicknames().add("One");
+        p.getNicknames().add("Two");
+        p.getNicknames().add("Three");
+
+        Map m = (Map)Models.toRaw(p);
+        Object o = m.get("nicknames");
+        assertNotNull(o, "List registered in the model");
+        assertEquals(o.getClass(), One.class);
+        One one = (One)o;
+
+
+        assertEquals(one.changes, 0, "No change");
+
+        p.getNicknames().subList(1, 2).clear();
+
+        assertEquals(p.getNicknames().size(), 2, "Two elements");
+
+        ListIterator<String> it = p.getNicknames().listIterator(0);
+        assertEquals(it.next(), "One");
+        assertEquals(it.next(), "Three");
+        assertFalse(it.hasNext());
+
+
+        assertEquals(one.changes, 1, "One change");
+    }
+
+    @Test public void sort() {
+        People p = Models.bind(new People(), c);
+        p.getNicknames().add("One");
+        p.getNicknames().add("Two");
+        p.getNicknames().add("Three");
+        p.getNicknames().add("Four");
+
+        Map m = (Map)Models.toRaw(p);
+        Object o = m.get("nicknames");
+        assertNotNull(o, "List registered in the model");
+        assertEquals(o.getClass(), One.class);
+        One one = (One)o;
+
+
+        assertEquals(one.changes, 0, "No change");
+
+        Collections.sort(p.getNicknames());
+
+        Iterator<String> it = p.getNicknames().iterator();
+        assertEquals(it.next(), "Four");
+        assertEquals(it.next(), "One");
+        assertEquals(it.next(), "Three");
+        assertEquals(it.next(), "Two");
+        assertFalse(it.hasNext());
+
+
+        assertNotEquals(one.changes, 0, "At least one change");
+
+        if (isJDK8()) {
+            assertEquals(one.changes, 1, "Exactly one echange");
+        }
+    }
+
+    @Test public void functionWithParameters() {
+        People p = Models.bind(new People(), c);
+        p.getNicknames().add("One");
+        p.getNicknames().add("Two");
+        p.getNicknames().add("Three");
+
+        Map m = (Map)Models.toRaw(p);
+        Object o = m.get("inInnerClass");
+        assertNotNull(o, "functiton is available");
+        assertEquals(o.getClass(), One.class);
+        One one = (One)o;
+        
+        Map<String,Object> obj = new HashMap<String, Object>();
+        obj.put("nick", "newNick");
+        obj.put("x", 42);
+        obj.put("y", 7.7f);
+        final Person data = new Person("a", "b", Sex.MALE);
+        
+        one.fb.call(data, obj);
+
+        assertEquals(p.getInfo().size(), 1, "a+b is there: " + p.getInfo());
+        assertEquals(p.getInfo().get(0), data, "Expecting data: " + p.getInfo());
+        
+        assertEquals(p.getNicknames().size(), 4, "One more nickname: " + p.getNicknames());
+        assertEquals(p.getNicknames().get(3), "newNick");
+        
+        assertEquals(p.getAge().size(), 2, "Two new values: " + p.getAge());
+        assertEquals(p.getAge().get(0).intValue(), 42);
+        assertEquals(p.getAge().get(1).intValue(), 7);
+    }
+    
+    @Test
+    public void addAge42ThreeTimes() {
+        People p = Models.bind(new People(), c);
+        Map m = (Map)Models.toRaw(p);
+        assertNotNull(m);
+        
+        class Inc implements Runnable {
+            int cnt;
+            
+            @Override
+            public void run() {
+                cnt++;
+            }
+        }
+        Inc incThreeTimes = new Inc();
+        p.onInfoChange(incThreeTimes);
+        
+        p.addAge42();
+        p.addAge42();
+        p.addAge42();
+        final int[] cnt = { 0, 0 };
+        p.readAddAgeCount(cnt, new Runnable() {
+            @Override
+            public void run() {
+                cnt[1] = 1;
+            }
+        });
+        assertEquals(cnt[1], 1, "Callback called");
+        assertEquals(cnt[0], 3, "Internal state kept");
+        assertEquals(incThreeTimes.cnt, 3, "Property change delivered three times");
+    }
+
+    private static boolean isJDK8() {
+        try {
+            Class.forName("java.lang.FunctionalInterface");
+            return true;
+        } catch (ClassNotFoundException ex) {
+            return false;
+        }
+    }
+    
+    static final class One {
+        int changes;
+        final PropertyBinding pb;
+        final FunctionBinding fb;
+    
+        One(Object m, PropertyBinding pb) throws NoSuchMethodException {
+            this.pb = pb;
+            this.fb = null;
+        }
+        One(Object m, FunctionBinding fb) throws NoSuchMethodException {
+            this.pb = null;
+            this.fb = fb;
+        }
+        
+        Object get() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+            return pb.getValue();
+        }
+        
+        void set(Object v) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+            pb.setValue(v);
+        }
+    }
+    
+    static final class MapTechnology 
+    implements Technology.ApplyId<Map<String,One>>, Transfer {
+        private Map<String, One> appliedData;
+        private String appliedId;
+
+        @Override
+        public Map<String, One> wrapModel(Object model) {
+            return new HashMap<String, One>();
+        }
+
+        @Override
+        public void valueHasMutated(Map<String, One> data, String propertyName) {
+            One p = data.get(propertyName);
+            if (p != null) {
+                p.changes++;
+            }
+        }
+
+        @Override
+        public void bind(PropertyBinding b, Object model, Map<String, One> data) {
+            try {
+                One o = new One(model, b);
+                data.put(b.getPropertyName(), o);
+            } catch (NoSuchMethodException ex) {
+                throw new IllegalStateException(ex);
+            }
+        }
+
+        @Override
+        public void expose(FunctionBinding fb, Object model, Map<String, One> data) {
+            try {
+                data.put(fb.getFunctionName(), new One(model, fb));
+            } catch (NoSuchMethodException ex) {
+                throw new IllegalStateException(ex);
+            }
+        }
+
+        @Override
+        public void applyBindings(Map<String, One> data) {
+            throw new UnsupportedOperationException("Never called!");
+        }
+
+        @Override
+        public Object wrapArray(Object[] arr) {
+            return arr;
+        }
+
+        @Override
+        public void extract(Object obj, String[] props, Object[] values) {
+            Map<?,?> map = obj instanceof Map ? (Map<?,?>)obj : null;
+            for (int i = 0; i < Math.min(props.length, values.length); i++) {
+                if (map == null) {
+                    values[i] = null;
+                } else {
+                    values[i] = map.get(props[i]);
+                    if (values[i] instanceof One) {
+                        values[i] = ((One)values[i]).pb.getValue();
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void loadJSON(JSONCall call) {
+            call.notifyError(new UnsupportedOperationException());
+        }
+
+        @Override
+        public <M> M toModel(Class<M> modelClass, Object data) {
+            return modelClass.cast(data);
+        }
+
+        @Override
+        public Object toJSON(InputStream is) throws IOException {
+            throw new IOException();
+        }
+
+        @Override
+        public void runSafe(Runnable r) {
+            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+        }
+
+        @Override
+        public void applyBindings(String id, Map<String, One> data) {
+            this.appliedId = id;
+            this.appliedData = data;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/net/java/html/json/ModelProcessorTest.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/net/java/html/json/ModelProcessorTest.java b/json/src/test/java/net/java/html/json/ModelProcessorTest.java
new file mode 100644
index 0000000..9c0d336
--- /dev/null
+++ b/json/src/test/java/net/java/html/json/ModelProcessorTest.java
@@ -0,0 +1,823 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json;
+
+import java.io.IOException;
+import java.util.Locale;
+import javax.tools.Diagnostic;
+import javax.tools.JavaFileObject;
+import static org.testng.Assert.*;
+import org.testng.annotations.Test;
+
+/** Verify errors emitted by the processor.
+ *
+ * @author Jaroslav Tulach
+ */
+public class ModelProcessorTest {
+    @Test public void verifyWrongType() throws IOException {
+        String html = "<html><body>"
+            + "</body></html>";
+        String code = "package x.y.z;\n"
+            + "import net.java.html.json.Model;\n"
+            + "import net.java.html.json.Property;\n"
+            + "@Model(className=\"XModel\", properties={\n"
+            + "  @Property(name=\"prop\", type=Runnable.class)\n"
+            + "})\n"
+            + "class X {\n"
+            + "}\n";
+
+        Compile c = Compile.create(html, code);
+        assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
+        boolean ok = false;
+        StringBuilder msgs = new StringBuilder();
+        for (Diagnostic<? extends JavaFileObject> e : c.getErrors()) {
+            String msg = e.getMessage(Locale.ENGLISH);
+            if (msg.contains("Runnable")) {
+                ok = true;
+            }
+            msgs.append("\n").append(msg);
+        }
+        if (!ok) {
+            fail("Should contain warning about Runnable:" + msgs);
+        }
+    }
+
+    @Test public void verifyWrongTypeInInnerClass() throws IOException {
+        String html = "<html><body>"
+            + "</body></html>";
+        String code = "package x.y.z;\n"
+            + "import net.java.html.json.Model;\n"
+            + "import net.java.html.json.Property;\n"
+            + "class X {\n"
+            + "  @Model(className=\"XModel\", properties={\n"
+            + "    @Property(name=\"prop\", type=Runnable.class)\n"
+            + "  })\n"
+            + "  static class Inner {\n"
+            + "  }\n"
+            + "}\n";
+
+        Compile c = Compile.create(html, code);
+        assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
+        boolean ok = false;
+        StringBuilder msgs = new StringBuilder();
+        for (Diagnostic<? extends JavaFileObject> e : c.getErrors()) {
+            String msg = e.getMessage(Locale.ENGLISH);
+            if (msg.contains("Runnable")) {
+                ok = true;
+            }
+            msgs.append("\n").append(msg);
+        }
+        if (!ok) {
+            fail("Should contain warning about Runnable:" + msgs);
+        }
+    }
+
+    @Test public void warnOnNonStatic() throws IOException {
+        String html = "<html><body>"
+            + "</body></html>";
+        String code = "package x.y.z;\n"
+            + "import net.java.html.json.Model;\n"
+            + "import net.java.html.json.Property;\n"
+            + "import net.java.html.json.ComputedProperty;\n"
+            + "@Model(className=\"XModel\", properties={\n"
+            + "  @Property(name=\"prop\", type=int.class)\n"
+            + "})\n"
+            + "class X {\n"
+            + "    @ComputedProperty int y(int prop) {\n"
+            + "        return prop;\n"
+            + "    }\n"
+            + "}\n";
+
+        Compile c = Compile.create(html, code);
+        assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
+        boolean ok = false;
+        StringBuilder msgs = new StringBuilder();
+        for (Diagnostic<? extends JavaFileObject> e : c.getErrors()) {
+            String msg = e.getMessage(Locale.ENGLISH);
+            if (msg.contains("y has to be static")) {
+                ok = true;
+            }
+            msgs.append("\n").append(msg);
+        }
+        if (!ok) {
+            fail("Should contain warning about non-static method:" + msgs);
+        }
+    }
+
+    @Test public void warnOnDuplicated() throws IOException {
+        String html = "<html><body>"
+            + "</body></html>";
+        String code = "package x.y.z;\n"
+            + "import net.java.html.json.Model;\n"
+            + "import net.java.html.json.Property;\n"
+            + "import net.java.html.json.ComputedProperty;\n"
+            + "@Model(className=\"XModel\", properties={\n"
+            + "  @Property(name=\"prop1\", type=int.class),\n"
+            + "  @Property(name=\"prop2\", type=int.class)\n"
+            + "})\n"
+            + "class X {\n"
+            + "    @ComputedProperty static int y(int prop1) {\n"
+            + "        return prop1;\n"
+            + "    }\n"
+            + "    @ComputedProperty static int y(int prop1, int prop2) {\n"
+            + "        return prop2;\n"
+            + "    }\n"
+            + "}\n";
+
+        Compile c = Compile.create(html, code);
+        assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
+        boolean ok = false;
+        StringBuilder msgs = new StringBuilder();
+        for (Diagnostic<? extends JavaFileObject> e : c.getErrors()) {
+            String msg = e.getMessage(Locale.ENGLISH);
+            if (msg.contains("Cannot have the property y defined twice")) {
+                ok = true;
+            }
+            msgs.append("\n").append(msg);
+        }
+        if (!ok) {
+            fail("Should contain warning duplicated property:" + msgs);
+        }
+    }
+
+    @Test public void warnOnDuplicatedWithNormalProp() throws IOException {
+        String html = "<html><body>"
+            + "</body></html>";
+        String code = "package x.y.z;\n"
+            + "import net.java.html.json.Model;\n"
+            + "import net.java.html.json.Property;\n"
+            + "import net.java.html.json.ComputedProperty;\n"
+            + "@Model(className=\"XModel\", properties={\n"
+            + "  @Property(name=\"prop1\", type=int.class),\n"
+            + "  @Property(name=\"prop2\", type=int.class)\n"
+            + "})\n"
+            + "class X {\n"
+            + "    @ComputedProperty static int prop2(int prop1) {\n"
+            + "        return prop1;\n"
+            + "    }\n"
+            + "}\n";
+
+        Compile c = Compile.create(html, code);
+        assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
+        boolean ok = false;
+        StringBuilder msgs = new StringBuilder();
+        for (Diagnostic<? extends JavaFileObject> e : c.getErrors()) {
+            String msg = e.getMessage(Locale.ENGLISH);
+            if (msg.contains("Cannot have the property prop2 defined twice")) {
+                ok = true;
+            }
+            msgs.append("\n").append(msg);
+        }
+        if (!ok) {
+            fail("Should contain warning duplicated property:" + msgs);
+        }
+    }
+
+    @Test public void tooManyProperties() throws IOException {
+        manyProperties(255, false, 0);
+    }
+
+    @Test public void tooManyArrayPropertiesIsOK() throws IOException {
+        manyProperties(0, true, 300);
+    }
+
+    @Test public void justEnoughProperties() throws IOException {
+        manyProperties(254, true, 0);
+    }
+
+    @Test public void justEnoughPropertiesWithArrayOne() throws IOException {
+        manyProperties(253, true, 300);
+    }
+
+    @Test public void justEnoughPropertiesButOneArrayOne() throws IOException {
+        manyProperties(254, false, 300);
+    }
+
+    private void manyProperties(
+        int cnt, boolean constructorWithParams, int arrayCnt
+    ) throws IOException {
+        String html = "<html><body>"
+            + "</body></html>";
+        StringBuilder code = new StringBuilder();
+        code.append("package x.y.z;\n"
+            + "import net.java.html.json.Model;\n"
+            + "import net.java.html.json.Property;\n"
+            + "@Model(className=\"XModel\", properties={\n"
+        );
+        for (int i = 1; i <= cnt; i++) {
+            code.append("  @Property(name=\"prop").append(i).append("\", ");
+            code.append("type=int.class),\n");
+        }
+        for (int i = 1; i <= arrayCnt; i++) {
+            code.append("  @Property(name=\"array").append(i).append("\", ");
+            code.append("array=true, ");
+            code.append("type=int.class),\n");
+        }
+        code.append(""
+            + "})\n"
+            + "class X {\n"
+            + "    static {\n"
+            + "      new XModel();\n"
+            + "      new XModel("
+        );
+        if (constructorWithParams) {
+            code.append("0");
+            for (int i = 1; i < cnt; i++) {
+                code.append(",\n").append(i);
+            }
+        }
+        code.append(");\n"
+            + "    }\n"
+            + "}\n"
+        );
+
+        Compile c = Compile.create(html, code.toString());
+        assertTrue(c.getErrors().isEmpty(), "Compiles OK: " + c.getErrors());
+    }
+
+    @Test public void writeableComputedPropertyMissingWrite() throws IOException {
+        String html = "<html><body>"
+            + "</body></html>";
+        String code = "package x.y.z;\n"
+            + "import net.java.html.json.Model;\n"
+            + "import net.java.html.json.Property;\n"
+            + "import net.java.html.json.ComputedProperty;\n"
+            + "@Model(className=\"XModel\", properties={\n"
+            + "  @Property(name=\"prop\", type=int.class)\n"
+            + "})\n"
+            + "class X {\n"
+            + "    static @ComputedProperty(write=\"setY\") int y(int prop) {\n"
+            + "        return prop;\n"
+            + "    }\n"
+            + "}\n";
+
+        Compile c = Compile.create(html, code);
+        assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
+        boolean ok = false;
+        StringBuilder msgs = new StringBuilder();
+        for (Diagnostic<? extends JavaFileObject> e : c.getErrors()) {
+            String msg = e.getMessage(Locale.ENGLISH);
+            if (msg.contains("Cannot find setY")) {
+                ok = true;
+            }
+            msgs.append("\n").append(msg);
+        }
+        if (!ok) {
+            fail("Should contain warning about non-static method:" + msgs);
+        }
+    }
+
+    @Test public void writeableComputedPropertyWrongWriteType() throws IOException {
+        String html = "<html><body>"
+            + "</body></html>";
+        String code = "package x.y.z;\n"
+            + "import net.java.html.json.Model;\n"
+            + "import net.java.html.json.Property;\n"
+            + "import net.java.html.json.ComputedProperty;\n"
+            + "@Model(className=\"XModel\", properties={\n"
+            + "  @Property(name=\"prop\", type=int.class)\n"
+            + "})\n"
+            + "class X {\n"
+            + "    static @ComputedProperty(write=\"setY\") int y(int prop) {\n"
+            + "        return prop;\n"
+            + "    }\n"
+            + "    static void setY(XModel model, String prop) {\n"
+            + "    }\n"
+            + "}\n";
+
+        Compile c = Compile.create(html, code);
+        assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
+        boolean ok = false;
+        StringBuilder msgs = new StringBuilder();
+        for (Diagnostic<? extends JavaFileObject> e : c.getErrors()) {
+            String msg = e.getMessage(Locale.ENGLISH);
+            if (msg.contains("Write method first argument needs to be XModel and second int or Object")) {
+                ok = true;
+            }
+            msgs.append("\n").append(msg);
+        }
+        if (!ok) {
+            fail("Should contain warning about non-static method:" + msgs);
+        }
+    }
+
+    @Test public void writeableComputedPropertyReturnsVoid() throws IOException {
+        String html = "<html><body>"
+            + "</body></html>";
+        String code = "package x.y.z;\n"
+            + "import net.java.html.json.Model;\n"
+            + "import net.java.html.json.Property;\n"
+            + "import net.java.html.json.ComputedProperty;\n"
+            + "@Model(className=\"XModel\", properties={\n"
+            + "  @Property(name=\"prop\", type=int.class)\n"
+            + "})\n"
+            + "class X {\n"
+            + "    static @ComputedProperty(write=\"setY\") int y(int prop) {\n"
+            + "        return prop;\n"
+            + "    }\n"
+            + "    static Number setY(XModel model, int prop) {\n"
+            + "    }\n"
+            + "}\n";
+
+        Compile c = Compile.create(html, code);
+        assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
+        boolean ok = false;
+        StringBuilder msgs = new StringBuilder();
+        for (Diagnostic<? extends JavaFileObject> e : c.getErrors()) {
+            String msg = e.getMessage(Locale.ENGLISH);
+            if (msg.contains("Write method has to return void")) {
+                ok = true;
+            }
+            msgs.append("\n").append(msg);
+        }
+        if (!ok) {
+            fail("Should contain warning about non-static method:" + msgs);
+        }
+    }
+
+    @Test public void computedCantReturnVoid() throws IOException {
+        String html = "<html><body>"
+            + "</body></html>";
+        String code = "package x.y.z;\n"
+            + "import net.java.html.json.Model;\n"
+            + "import net.java.html.json.Property;\n"
+            + "import net.java.html.json.ComputedProperty;\n"
+            + "@Model(className=\"XModel\", properties={\n"
+            + "  @Property(name=\"prop\", type=int.class)\n"
+            + "})\n"
+            + "class X {\n"
+            + "    @ComputedProperty static void y(int prop) {\n"
+            + "    }\n"
+            + "}\n";
+
+        Compile c = Compile.create(html, code);
+        assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
+        boolean ok = false;
+        StringBuilder msgs = new StringBuilder();
+        for (Diagnostic<? extends JavaFileObject> e : c.getErrors()) {
+            String msg = e.getMessage(Locale.ENGLISH);
+            if (msg.contains("y cannot return void")) {
+                ok = true;
+            }
+            msgs.append("\n").append(msg);
+        }
+        if (!ok) {
+            fail("Should contain warning about non-static method:" + msgs);
+        }
+    }
+
+    @Test public void computedCantReturnRunnable() throws IOException {
+        String html = "<html><body>"
+            + "</body></html>";
+        String code = "package x.y.z;\n"
+            + "import net.java.html.json.Model;\n"
+            + "import net.java.html.json.Property;\n"
+            + "import net.java.html.json.ComputedProperty;\n"
+            + "@Model(className=\"XModel\", properties={\n"
+            + "  @Property(name=\"prop\", type=int.class)\n"
+            + "})\n"
+            + "class X {\n"
+            + "    @ComputedProperty static Runnable y(int prop) {\n"
+            + "       return null;\n"
+            + "    }\n"
+            + "}\n";
+
+        Compile c = Compile.create(html, code);
+        assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
+        boolean ok = false;
+        StringBuilder msgs = new StringBuilder();
+        for (Diagnostic<? extends JavaFileObject> e : c.getErrors()) {
+            String msg = e.getMessage(Locale.ENGLISH);
+            if (msg.contains("y cannot return java.lang.Runnable")) {
+                ok = true;
+            }
+            msgs.append("\n").append(msg);
+        }
+        if (!ok) {
+            fail("Should contain warning about non-static method:" + msgs);
+        }
+    }
+
+    @Test public void canWeCompileWithJDK1_5SourceLevel() throws IOException {
+        String html = "<html><body>"
+            + "</body></html>";
+        String code = "package x.y.z;\n"
+            + "import net.java.html.json.Model;\n"
+            + "import net.java.html.json.Property;\n"
+            + "import net.java.html.json.ComputedProperty;\n"
+            + "@Model(className=\"XModel\", properties={\n"
+            + "  @Property(name=\"prop\", type=long.class)\n"
+            + "})\n"
+            + "class X {\n"
+            + "  @ComputedProperty static double derived(long prop) { return prop; }"
+            + "}\n";
+
+        Compile c = Compile.create(html, code, "1.5");
+        assertTrue(c.getErrors().isEmpty(), "No errors: " + c.getErrors());
+    }
+    
+    @Test public void instanceNeedsDefaultConstructor() throws IOException {
+        String html = "<html><body>"
+            + "</body></html>";
+        String code = "package x.y.z;\n"
+            + "import net.java.html.json.Model;\n"
+            + "import net.java.html.json.Property;\n"
+            + "import net.java.html.json.ComputedProperty;\n"
+            + "@Model(className=\"XModel\", instance=true, properties={\n"
+            + "  @Property(name=\"prop\", type=long.class)\n"
+            + "})\n"
+            + "class X {\n"
+            + "  X(int x) {}\n"
+            + "}\n";
+
+        Compile c = Compile.create(html, code);
+        c.assertError("Needs non-private default constructor when instance=true");
+    }
+    
+    @Test public void instanceNeedsNonPrivateConstructor() throws IOException {
+        String html = "<html><body>"
+            + "</body></html>";
+        String code = "package x.y.z;\n"
+            + "import net.java.html.json.Model;\n"
+            + "import net.java.html.json.Property;\n"
+            + "import net.java.html.json.ComputedProperty;\n"
+            + "@Model(className=\"XModel\", instance=true, properties={\n"
+            + "  @Property(name=\"prop\", type=long.class)\n"
+            + "})\n"
+            + "class X {\n"
+            + "  private X() {}\n"
+            + "}\n";
+
+        Compile c = Compile.create(html, code);
+        c.assertError("Needs non-private default constructor when instance=true");
+    }
+
+    @Test public void instanceNoConstructorIsOK() throws IOException {
+        String html = "<html><body>"
+            + "</body></html>";
+        String code = "package x.y.z;\n"
+            + "import net.java.html.json.Model;\n"
+            + "import net.java.html.json.Property;\n"
+            + "import net.java.html.json.ComputedProperty;\n"
+            + "@Model(className=\"XModel\", instance=true, properties={\n"
+            + "  @Property(name=\"prop\", type=long.class)\n"
+            + "})\n"
+            + "class X {\n"
+            + "}\n";
+
+        Compile c = Compile.create(html, code);
+        c.assertNoErrors();
+    }
+
+    @Test public void putNeedsDataArgument() throws Exception {
+        needsAnArg("PUT");
+    }
+
+    @Test public void postNeedsDataArgument() throws Exception {
+        needsAnArg("POST");
+    }
+
+    private void needsAnArg(String method) throws Exception {
+        String html = "<html><body>"
+            + "</body></html>";
+        String code = "package x.y.z;\n"
+            + "import net.java.html.json.Model;\n"
+            + "import net.java.html.json.Property;\n"
+            + "import net.java.html.json.OnReceive;\n"
+            + "@Model(className=\"XModel\", properties={\n"
+            + "  @Property(name=\"prop\", type=long.class)\n"
+            + "})\n"
+            + "class X {\n"
+            + "  @Model(className=\"PQ\", properties={})\n"
+            + "  class PImpl {\n"
+            + "  }\n"
+            + "  @OnReceive(method=\"" + method + "\", url=\"whereever\")\n"
+            + "  static void obtained(XModel m, PQ p) { }\n"
+            + "}\n";
+
+        Compile c = Compile.create(html, code);
+        assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
+        for (Diagnostic<? extends JavaFileObject> diagnostic : c.getErrors()) {
+            String msg = diagnostic.getMessage(Locale.ENGLISH);
+            if (msg.contains("specify a data()")) {
+                return;
+            }
+        }
+        fail("Needs an error message about missing data():\n" + c.getErrors());
+
+    }
+
+
+    @Test public void jsonNeedsToUseGet () throws Exception {
+        String html = "<html><body>"
+            + "</body></html>";
+        String code = "package x.y.z;\n"
+            + "import net.java.html.json.Model;\n"
+            + "import net.java.html.json.Property;\n"
+            + "import net.java.html.json.OnReceive;\n"
+            + "@Model(className=\"XModel\", properties={\n"
+            + "  @Property(name=\"prop\", type=long.class)\n"
+            + "})\n"
+            + "class X {\n"
+            + "  @Model(className=\"PQ\", properties={})\n"
+            + "  class PImpl {\n"
+            + "  }\n"
+            + "  @OnReceive(method=\"POST\", jsonp=\"callback\", url=\"whereever\")\n"
+            + "  static void obtained(XModel m, PQ p) { }\n"
+            + "}\n";
+
+        Compile c = Compile.create(html, code);
+        assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
+        for (Diagnostic<? extends JavaFileObject> diagnostic : c.getErrors()) {
+            String msg = diagnostic.getMessage(Locale.ENGLISH);
+            if (msg.contains("JSONP works only with GET")) {
+                return;
+            }
+        }
+        fail("Needs an error message about wrong method:\n" + c.getErrors());
+
+    }
+
+    @Test public void noHeadersForWebSockets() throws Exception {
+        String html = "<html><body>"
+            + "</body></html>";
+        String code = "package x.y.z;\n"
+            + "import net.java.html.json.Model;\n"
+            + "import net.java.html.json.Property;\n"
+            + "import net.java.html.json.OnReceive;\n"
+            + "@Model(className=\"XModel\", properties={\n"
+            + "  @Property(name=\"prop\", type=long.class)\n"
+            + "})\n"
+            + "class X {\n"
+            + "  @Model(className=\"PQ\", properties={})\n"
+            + "  class PImpl {\n"
+            + "  }\n"
+            + "  @OnReceive(method=\"WebSocket\", data = PQ.class, headers=\"SomeHeader: {some}\", url=\"whereever\")\n"
+            + "  static void obtained(XModel m, PQ p) { }\n"
+            + "}\n";
+
+        Compile c = Compile.create(html, code);
+        assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
+        for (Diagnostic<? extends JavaFileObject> diagnostic : c.getErrors()) {
+            String msg = diagnostic.getMessage(Locale.ENGLISH);
+            if (msg.contains("WebSocket spec does not support headers")) {
+                return;
+            }
+        }
+        fail("Needs an error message about headers:\n" + c.getErrors());
+
+    }
+
+    @Test public void webSocketsWithoutDataIsError() throws Exception {
+        String html = "<html><body>"
+            + "</body></html>";
+        String code = "package x.y.z;\n"
+            + "import net.java.html.json.Model;\n"
+            + "import net.java.html.json.Property;\n"
+            + "import net.java.html.json.OnReceive;\n"
+            + "@Model(className=\"XModel\", properties={\n"
+            + "  @Property(name=\"prop\", type=long.class)\n"
+            + "})\n"
+            + "class X {\n"
+            + "  @Model(className=\"PQ\", properties={})\n"
+            + "  class PImpl {\n"
+            + "  }\n"
+            + "  @OnReceive(method=\"WebSocket\", url=\"whereever\")\n"
+            + "  static void obtained(XModel m, PQ p) { }\n"
+            + "}\n";
+
+        Compile c = Compile.create(html, code);
+        assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
+        for (Diagnostic<? extends JavaFileObject> diagnostic : c.getErrors()) {
+            String msg = diagnostic.getMessage(Locale.ENGLISH);
+            if (msg.contains("eeds to specify a data()")) {
+                return;
+            }
+        }
+        fail("Needs data attribute :\n" + c.getErrors());
+    }
+
+    @Test public void noNewLinesInHeaderLines() throws Exception {
+        String html = "<html><body>"
+            + "</body></html>";
+        String code = "package x.y.z;\n"
+            + "import net.java.html.json.Model;\n"
+            + "import net.java.html.json.Property;\n"
+            + "import net.java.html.json.OnReceive;\n"
+            + "@Model(className=\"XModel\", properties={\n"
+            + "  @Property(name=\"prop\", type=long.class)\n"
+            + "})\n"
+            + "class X {\n"
+            + "  @Model(className=\"PQ\", properties={})\n"
+            + "  class PImpl {\n"
+            + "  }\n"
+            + "  @OnReceive(headers=\"SomeHeader\\n: {some}\", url=\"whereever\")\n"
+            + "  static void obtained(XModel m, PQ p) { }\n"
+            + "}\n";
+
+        Compile c = Compile.create(html, code);
+        assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
+        for (Diagnostic<? extends JavaFileObject> diagnostic : c.getErrors()) {
+            String msg = diagnostic.getMessage(Locale.ENGLISH);
+            if (msg.contains("Header line cannot contain line separator")) {
+                return;
+            }
+        }
+        fail("Needs an error message about headers:\n" + c.getErrors());
+
+    }
+
+    @Test public void noReturnInHeaderLines() throws Exception {
+        String html = "<html><body>"
+            + "</body></html>";
+        String code = "package x.y.z;\n"
+            + "import net.java.html.json.Model;\n"
+            + "import net.java.html.json.Property;\n"
+            + "import net.java.html.json.OnReceive;\n"
+            + "@Model(className=\"XModel\", properties={\n"
+            + "  @Property(name=\"prop\", type=long.class)\n"
+            + "})\n"
+            + "class X {\n"
+            + "  @Model(className=\"PQ\", properties={})\n"
+            + "  class PImpl {\n"
+            + "  }\n"
+            + "  @OnReceive(headers=\"Some\\rHeader: {some}\", url=\"whereever\")\n"
+            + "  static void obtained(XModel m, PQ p) { }\n"
+            + "}\n";
+
+        Compile c = Compile.create(html, code);
+        assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
+        for (Diagnostic<? extends JavaFileObject> diagnostic : c.getErrors()) {
+            String msg = diagnostic.getMessage(Locale.ENGLISH);
+            if (msg.contains("Header line cannot contain line separator")) {
+                return;
+            }
+        }
+        fail("Needs an error message about headers:\n" + c.getErrors());
+
+    }
+
+    @Test public void onErrorHasToExist() throws IOException {
+        Compile res = Compile.create("", "package x;\n"
+            + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
+            + "  @net.java.html.json.Property(name=\"x\", type=String.class)\n"
+            + "})\n"
+            + "class UseOnReceive {\n"
+            + "  @net.java.html.json.OnReceive(url=\"http://nowhere.com\", onError=\"doesNotExist\")\n"
+            + "  static void onMessage(MyModel model, String value) {\n"
+            + "  }\n"
+            + "}\n"
+        );
+        res.assertErrors();
+        res.assertError("not find doesNotExist");
+    }
+
+    @Test public void usingListIsOK() throws IOException {
+        Compile res = Compile.create("", "package x;\n"
+            + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
+            + "  @net.java.html.json.Property(name=\"x\", type=String.class)\n"
+            + "})\n"
+            + "class UseOnReceive {\n"
+            + "  @net.java.html.json.OnReceive(url=\"http://nowhere.com\")\n"
+            + "  static void onMessage(MyModel model, java.util.List<MyData> value) {\n"
+            + "  }\n"
+            + "\n"
+            + "  @net.java.html.json.Model(className=\"MyData\", properties={\n"
+            + "  })\n"
+            + "  static class MyDataModel {\n"
+            + "  }\n"
+            + "}\n"
+        );
+        res.assertNoErrors();
+    }
+
+    @Test public void functionAndPropertyCollide() throws IOException {
+        Compile res = Compile.create("", "package x;\n"
+            + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
+            + "  @net.java.html.json.Property(name=\"x\", type=String.class)\n"
+            + "})\n"
+            + "class Collision {\n"
+            + "  @net.java.html.json.Function\n"
+            + "  static void x(MyModel model, String value) {\n"
+            + "  }\n"
+            + "}\n"
+        );
+        res.assertErrors();
+        res.assertError("cannot have the name");
+    }
+
+    @Test public void twoPropertiesCollide() throws IOException {
+        Compile res = Compile.create("", "package x;\n"
+            + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
+            + "  @net.java.html.json.Property(name=\"x\", type=String.class),\n"
+            + "  @net.java.html.json.Property(name=\"x\", type=int.class)\n"
+            + "})\n"
+            + "class Collision {\n"
+            + "}\n"
+        );
+        res.assertErrors();
+        res.assertError("Cannot have the property");
+    }
+
+    @Test public void propertyAndComputedOneCollide() throws IOException {
+        Compile res = Compile.create("", "package x;\n"
+            + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
+            + "  @net.java.html.json.Property(name=\"x\", type=String.class),\n"
+            + "})\n"
+            + "class Collision {\n"
+            + "  @net.java.html.json.ComputedProperty static int x(String x) {\n"
+            + "    return x.length();\n"
+            + "  }\n"
+            + "}\n"
+        );
+        res.assertErrors();
+        res.assertError("Cannot have the property");
+    }
+
+    @Test public void onWebSocketJustTwoArgs() throws IOException {
+        Compile res = Compile.create("", "package x;\n"
+            + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
+            + "  @net.java.html.json.Property(name=\"x\", type=String.class)\n"
+            + "})\n"
+            + "class UseOnReceive {\n"
+            + "  @net.java.html.json.OnReceive(url=\"http://nowhere.com\", method=\"WebSocket\", data=String.class)\n"
+            + "  static void onMessage(MyModel model, String value, int arg) {\n"
+            + "  }\n"
+            + "}\n"
+        );
+        res.assertErrors();
+        res.assertError("only have two arg");
+    }
+
+    @Test public void onErrorWouldHaveToBeStatic() throws IOException {
+        Compile res = Compile.create("", "package x;\n"
+            + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
+            + "  @net.java.html.json.Property(name=\"x\", type=String.class)\n"
+            + "})\n"
+            + "class UseOnReceive {\n"
+            + "  @net.java.html.json.OnReceive(url=\"http://nowhere.com\", onError=\"notStatic\")\n"
+            + "  static void onMessage(MyModel model, String value) {\n"
+            + "  }\n"
+            + "  void notStatic(Exception e) {}\n"
+            + "}\n"
+        );
+        res.assertErrors();
+        res.assertError("have to be static");
+    }
+
+    @Test public void onErrorMustAcceptExceptionArgument() throws IOException {
+        Compile res = Compile.create("", "package x;\n"
+            + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
+            + "  @net.java.html.json.Property(name=\"x\", type=String.class)\n"
+            + "})\n"
+            + "class UseOnReceive {\n"
+            + "  @net.java.html.json.OnReceive(url=\"http://nowhere.com\", onError=\"subclass\")\n"
+            + "  static void onMessage(MyModel model, String value) {\n"
+            + "  }\n"
+            + "  static void subclass(java.io.IOException e) {}\n"
+            + "}\n"
+        );
+        res.assertErrors();
+        res.assertError("Error method first argument needs to be MyModel and second Exception");
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/net/java/html/json/ModelTest.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/net/java/html/json/ModelTest.java b/json/src/test/java/net/java/html/json/ModelTest.java
new file mode 100644
index 0000000..bb4a7be
--- /dev/null
+++ b/json/src/test/java/net/java/html/json/ModelTest.java
@@ -0,0 +1,506 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.concurrent.Executor;
+import net.java.html.BrwsrCtx;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.json.spi.FunctionBinding;
+import org.netbeans.html.json.spi.PropertyBinding;
+import org.netbeans.html.json.spi.Technology;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertSame;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+@Model(className = "Modelik", builder = "change", targetId = "", properties = {
+    @Property(name = "value", type = int.class),
+    @Property(name = "count", type = int.class),
+    @Property(name = "unrelated", type = long.class),
+    @Property(name = "names", type = String.class, array = true),
+    @Property(name = "values", type = int.class, array = true),
+    @Property(name = "people", type = Person.class, array = true),
+    @Property(name = "changedProperty", type=String.class)
+})
+public class ModelTest {
+    private MockTechnology my;
+    private Modelik model;
+    private static Modelik leakedModel;
+
+    @BeforeMethod
+    public void createModel() {
+        my = new MockTechnology();
+        final BrwsrCtx c = Contexts.newBuilder().register(Technology.class, my, 1).build();
+        model = Models.bind(new Modelik(), c);
+    }
+
+    @Test public void classGeneratedWithSetterGetter() {
+        model.setValue(10);
+        assertEquals(10, model.getValue(), "Value changed");
+    }
+
+    @Test public void computedMethod() {
+        model.setValue(4);
+        assertEquals(16, model.getPowerValue());
+    }
+
+    @Test public void equalsAndHashCode() {
+        Modelik m1 = new Modelik();
+        m1.setValue(10);
+        m1.setCount(20);
+        m1.setUnrelated(30);
+        m1.setChangedProperty("changed");
+        m1.getNames().add("firstName");
+        Modelik m2 = new Modelik().
+            changeValue(10).
+            changeCount(20).
+            changeUnrelated(30).
+            changeChangedProperty("changed").
+            changeNames("firstName");
+
+        assertTrue(m1.equals(m2), "They are the same");
+        assertEquals(m1.hashCode(), m2.hashCode(), "Hashcode is the same");
+
+        m1.setCount(33);
+
+        assertFalse(m1.equals(m2), "No longer the same");
+        assertFalse(m1.hashCode() == m2.hashCode(), "No longe is hashcode is the same");
+    }
+
+    @Test public void arrayIsMutable() {
+        assertEquals(model.getNames().size(), 0, "Is empty");
+        model.getNames().add("Jarda");
+        assertEquals(model.getNames().size(), 1, "One element");
+    }
+
+    @Test public void arrayChangesNotNotifiedUntilInitied() {
+        model.getNames().add("Hello");
+        assertTrue(my.mutated.isEmpty(), "No change now " + my.mutated);
+        model.getNames().remove("Hello");
+        assertTrue(my.mutated.isEmpty(), "No change still " + my.mutated);
+        assertTrue(model.getNames().isEmpty(), "No empty");
+    }
+
+    @Test public void arrayChangesNotified() {
+        Models.applyBindings(model);
+        model.getNames().add("Hello");
+
+        assertFalse(my.mutated.isEmpty(), "There was a change" + my.mutated);
+        assertTrue(my.mutated.contains("names"), "Change in names property: " + my.mutated);
+
+        my.mutated.clear();
+
+        Iterator<String> it = model.getNames().iterator();
+        assertEquals(it.next(), "Hello");
+        it.remove();
+
+        assertFalse(my.mutated.isEmpty(), "There was a change" + my.mutated);
+        assertTrue(my.mutated.contains("names"), "Change in names property: " + my.mutated);
+
+        my.mutated.clear();
+
+        ListIterator<String> lit = model.getNames().listIterator();
+        lit.add("Jarda");
+
+        assertFalse(my.mutated.isEmpty(), "There was a change" + my.mutated);
+        assertTrue(my.mutated.contains("names"), "Change in names property: " + my.mutated);
+    }
+
+    @Test public void autoboxedArray() {
+        model.getValues().add(10);
+
+        assertEquals(model.getValues().get(0), Integer.valueOf(10), "Really ten");
+    }
+
+    @Test public void derivedArrayProp() {
+        model.applyBindings();
+        model.setCount(10);
+
+        List<String> arr = model.getRepeat();
+        assertEquals(arr.size(), 10, "Ten items: " + arr);
+
+        my.mutated.clear();
+
+        model.setCount(5);
+
+        arr = model.getRepeat();
+        assertEquals(arr.size(), 5, "Five items: " + arr);
+
+        assertEquals(my.mutated.size(), 2, "Two properties changed: " + my.mutated);
+        assertTrue(my.mutated.contains("repeat"), "Array is in there: " + my.mutated);
+        assertTrue(my.mutated.contains("count"), "Count is in there: " + my.mutated);
+    }
+
+    @Test public void derivedArrayPropChange() {
+        model.applyBindings();
+        model.setCount(5);
+
+        List<String> arr = model.getRepeat();
+        assertEquals(arr.size(), 5, "Five items: " + arr);
+
+        model.setRepeat(10);
+        assertEquals(model.getCount(), 10, "Changing repeat changes count");
+    }
+
+    @Test public void derivedPropertiesAreNotified() {
+        model.applyBindings();
+
+        model.setValue(33);
+
+        // not interested in change of this property
+        my.mutated.remove("changedProperty");
+
+        assertEquals(my.mutated.size(), 2, "Two properties changed: " + my.mutated);
+        assertTrue(my.mutated.contains("powerValue"), "Power value is in there: " + my.mutated);
+        assertTrue(my.mutated.contains("value"), "Simple value is in there: " + my.mutated);
+
+        my.mutated.clear();
+
+        model.setUnrelated(44);
+
+
+        // not interested in change of this property
+        my.mutated.remove("changedProperty");
+        assertEquals(my.mutated.size(), 1, "One property changed: " + my.mutated);
+        assertTrue(my.mutated.contains("unrelated"), "Its name is unrelated");
+    }
+
+    @Test public void computedPropertyCannotWriteToModel() {
+        leakedModel = model;
+        try {
+            String res = model.getNotAllowedWrite();
+            fail("We should not be allowed to write to the model: " + res);
+        } catch (IllegalStateException ex) {
+            // OK, we can't read
+        }
+    }
+
+    @Test public void computedPropertyCannotReadToModel() {
+        leakedModel = model;
+        try {
+            String res = model.getNotAllowedRead();
+            fail("We should not be allowed to read from the model: " + res);
+        } catch (IllegalStateException ex) {
+            // OK, we can't read
+        }
+    }
+
+    @OnReceive(url = "{protocol}://{host}?query={query}", data = Person.class, onError = "errorState")
+    static void loadPeople(Modelik thiz, People p) {
+        Modelik m = null;
+        m.applyBindings();
+        m.loadPeople("http", "apidesign.org", "query", new Person());
+    }
+
+    static void errorState(Modelik thiz, Exception ex) {
+
+    }
+
+    @OnReceive(url="{url}", headers={
+        "Easy: {easy}",
+        "H-a+r!d?e.r: {harder}",
+        "H-a+r!d?e's\"t: {harder}",
+        "Repeat-ed: {rep}",
+        "Repeat+ed: {rep}",
+        "Same-URL: {url}"
+    })
+    static void fetchPeopleWithHeaders(Modelik model, People p) {
+        model.fetchPeopleWithHeaders("url", "easy", "harder", "rep");
+    }
+
+    @OnReceive(url = "{protocol}://{host}?callback={back}&query={query}", jsonp = "back")
+    static void loadPeopleViaJSONP(Modelik thiz, People p) {
+        Modelik m = null;
+        m.applyBindings();
+        m.loadPeopleViaJSONP("http", "apidesign.org", "query");
+    }
+
+    @OnReceive(url = "{rep}://{rep}")
+    static void repeatedTest(Modelik thiz, People p) {
+        thiz.repeatedTest("justOneParameterRep");
+    }
+
+    @Function
+    static void doSomething() {
+    }
+
+    @ComputedProperty(write = "setPowerValue")
+    static int powerValue(int value) {
+        return value * value;
+    }
+
+    static void setPowerValue(Modelik m, int value) {
+        m.setValue((int)Math.sqrt(value));
+    }
+
+    @OnPropertyChange({ "powerValue", "unrelated" })
+    static void aPropertyChanged(Modelik m, String name) {
+        m.setChangedProperty(name);
+    }
+
+    @OnPropertyChange({ "values" })
+    static void anArrayPropertyChanged(String name, Modelik m) {
+        m.setChangedProperty(name);
+    }
+
+    @Test public void changeAnything() {
+        model.setCount(44);
+        assertNull(model.getChangedProperty(), "No observed value change");
+    }
+    @Test public void changeValue() {
+        model.setValue(33);
+        assertEquals(model.getChangedProperty(), "powerValue", "power property changed");
+    }
+    @Test public void changePowerValue() {
+        model.setValue(3);
+        assertEquals(model.getPowerValue(), 9, "Square");
+        model.setPowerValue(16);
+        assertEquals(model.getValue(), 4, "Square root");
+        assertEquals(model.getPowerValue(), 16, "Square changed");
+    }
+    @Test public void changeUnrelated() {
+        model.setUnrelated(333);
+        assertEquals(model.getChangedProperty(), "unrelated", "unrelated changed");
+    }
+
+    @Test public void changeInArray() {
+        model.getValues().add(10);
+        assertNull(model.getChangedProperty(), "No change before applyBindings");
+        model.applyBindings();
+        model.getValues().add(10);
+        assertEquals(model.getChangedProperty(), "values", "Something added into the array");
+    }
+
+    @ComputedProperty
+    static String notAllowedRead() {
+        return "Not allowed callback: " + leakedModel.getUnrelated();
+    }
+
+    @ComputedProperty
+    static String notAllowedWrite() {
+        leakedModel.setUnrelated(11);
+        return "Not allowed callback!";
+    }
+
+    @ComputedProperty(write="parseRepeat")
+    static List<String> repeat(int count) {
+        return Collections.nCopies(count, "Hello");
+    }
+    static void parseRepeat(Modelik m, Object v) {
+        m.setCount((Integer)v);
+    }
+
+    public @Test void hasPersonPropertyAndComputedFullName() {
+        List<Person> arr = model.getPeople();
+        assertEquals(arr.size(), 0, "By default empty");
+        Person p = null;
+        if (p != null) {
+            String fullNameGenerated = p.getFullName();
+            assertNotNull(fullNameGenerated);
+        }
+    }
+
+    public @Test void computedListIsOfTypeString() {
+        Person p = new Person("1st", "2nd", Sex.MALE);
+        String first = p.getBothNames().get(0);
+        String last = p.getBothNames().get(1);
+        assertEquals(first, "1st");
+        assertEquals(last, "2nd");
+    }
+    
+    @Model(className = "Inner", instance = true, properties = {
+        @Property(name = "x", type = int.class),
+        @Property(name = "y", type = int.class)
+    })
+    static final class InnerCntrl {
+        private BrwsrCtx ctx;
+
+        @ModelOperation
+        void init(Inner model, BrwsrCtx ctx) {
+            this.ctx = ctx;
+        }
+
+        @Function
+        void setYToTen(Inner model) {
+            assertCtx();
+            model.setY(10);
+        }
+
+        @ModelOperation
+        void modelYToTen(Inner model) {
+            assertCtx();
+            model.setY(10);
+        }
+
+        @OnPropertyChange("y")
+        void increment(Inner model) {
+            model.setX(model.getX() + 1);
+            assertCtx();
+        }
+
+        private void assertCtx() {
+            BrwsrCtx realCtx = BrwsrCtx.findDefault(InnerCntrl.class);
+            assertSame(realCtx, ctx, "Proper Ctx is provided");
+        }
+    }
+
+//   directly using setters doesn't set the context currently
+//    @Test
+//    public void incrementXOnChangeOfY() {
+//        doIncreementXOnChangeOfY(0);
+//    }
+
+    @Test
+    public void incrementXOnChangeOfYViaFunction() {
+        doIncreementXOnChangeOfY(1);
+    }
+
+    @Test
+    public void incrementXOnChangeOfYViaModel() {
+        doIncreementXOnChangeOfY(2);
+    }
+
+    private void doIncreementXOnChangeOfY(int modificationType) {
+        class Exec implements Executor {
+            int cnt;
+            @Override
+            public void execute(Runnable command) {
+                cnt++;
+                command.run();
+            }
+
+            final void assertCount(int expected, String msg) {
+                assertEquals(cnt, expected, msg);
+                cnt = 0;
+            }
+        }
+        MapModelTest.MapTechnology tech = new MapModelTest.MapTechnology();
+        Exec exec = new Exec();
+        final BrwsrCtx c = Contexts.newBuilder().
+            register(Technology.class, tech, 1).
+            register(Executor.class, exec, 5).
+            build();
+
+        Inner model = Models.bind(new Inner(), c);
+        exec.assertCount(0, "Executor not used for anything yet");
+        model.init(c);
+        exec.assertCount(1, "Executor used for initialization");
+        
+        Models.applyBindings(model);
+
+        assertEquals(model.getX(), 0, "Zero");
+        assertEquals(model.getY(), 0, "Zero too");
+        int execUse;
+        if (modificationType != 1) {
+            model.modelYToTen();
+            execUse = 3;
+        } else {
+            Object raw = Models.toRaw(model);
+            assertTrue(raw instanceof Map);
+            Map<?,?> map = (Map<?,?>) raw;
+            MapModelTest.One one = (MapModelTest.One) map.get("setYToTen");
+            assertNotNull(one);
+            one.fb.call(model, null);
+            execUse = 3;
+        }
+        assertEquals(model.getX(), 1, "One");
+        assertEquals(model.getY(), 10, "Ten");
+        exec.assertCount(execUse, "Executor used");
+    }
+
+    private static class MockTechnology implements Technology<Object> {
+        private final List<String> mutated = new ArrayList<String>();
+
+        @Override
+        public Object wrapModel(Object model) {
+            return this;
+        }
+
+        @Override
+        public void valueHasMutated(Object data, String propertyName) {
+            mutated.add(propertyName);
+        }
+
+        @Override
+        public void bind(PropertyBinding b, Object model, Object data) {
+        }
+
+        @Override
+        public void expose(FunctionBinding fb, Object model, Object d) {
+        }
+
+        @Override
+        public void applyBindings(Object data) {
+        }
+
+        @Override
+        public Object wrapArray(Object[] arr) {
+            return arr;
+        }
+
+        @Override
+        public <M> M toModel(Class<M> modelClass, Object data) {
+            return modelClass.cast(data);
+        }
+
+        @Override
+        public void runSafe(Runnable r) {
+            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/net/java/html/json/ModelsTest.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/net/java/html/json/ModelsTest.java b/json/src/test/java/net/java/html/json/ModelsTest.java
new file mode 100644
index 0000000..7e7c5b0
--- /dev/null
+++ b/json/src/test/java/net/java/html/json/ModelsTest.java
@@ -0,0 +1,72 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json;
+
+import static org.testng.Assert.*;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class ModelsTest {
+    
+    public ModelsTest() {
+    }
+
+    @Test public void peopleAreModel() {
+        assertTrue(Models.isModel(People.class), "People are generated class");
+    }
+    
+    @Test public void personIsModel() {
+        assertTrue(Models.isModel(Person.class), "Person is generated class");
+    }
+
+    @Test public void implClassIsNotModel() {
+        assertFalse(Models.isModel(PersonImpl.class), "Impl is not model");
+    }
+
+    @Test public void randomClassIsNotModel() {
+        assertFalse(Models.isModel(StringBuilder.class), "JDK classes are not model");
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/net/java/html/json/OperationTest.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/net/java/html/json/OperationTest.java b/json/src/test/java/net/java/html/json/OperationTest.java
new file mode 100644
index 0000000..db30837
--- /dev/null
+++ b/json/src/test/java/net/java/html/json/OperationTest.java
@@ -0,0 +1,125 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json;
+
+import java.io.IOException;
+import java.util.Arrays;
+import net.java.html.BrwsrCtx;
+import org.netbeans.html.context.spi.Contexts;
+import static org.testng.Assert.*;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+@Model(className = "OpModel", properties = {
+    @Property(name = "names", type = String.class, array = true)
+})
+public class OperationTest {
+    @ModelOperation static void add(OpModel m, String name, BrwsrCtx exp) {
+        assertSame(BrwsrCtx.findDefault(OpModel.class), exp, "Context is passed in");
+        m.getNames().add(name);
+    }
+
+    @ModelOperation static void add(OpModel m, int times, String name) throws IOException {
+        while (times-- > 0) {
+            m.getNames().add(name.toUpperCase());
+        }
+    }
+    
+    @ModelOperation static void copy(OpModel m, OpModel orig) {
+        m.getNames().clear();
+        m.getNames().addAll(orig.getNames());
+    }
+
+    @Test public void addOneToTheModel() {
+        BrwsrCtx ctx = Contexts.newBuilder().build();
+        OpModel m = Models.bind(new OpModel("One"), ctx);
+        m.add("Second", ctx);
+        assertEquals(m.getNames().size(), 2, "Both are there: " + m.getNames());
+    }
+
+    @Test public void addTwoUpperCasesToTheModel() {
+        BrwsrCtx ctx = Contexts.newBuilder().build();
+        OpModel m = Models.bind(new OpModel("One"), ctx);
+        m.add(2, "Second");
+        assertEquals(m.getNames().size(), 3, "Both are there: " + m.getNames());
+        assertEquals(m.getNames().get(1), "SECOND", "Converted to upper case");
+        assertEquals(m.getNames().get(2), "SECOND", "Also converted to upper case");
+    }
+    
+    @Test public void noAnnonymousInnerClass() {
+        int cnt = 0;
+        for (Class<?> c : OpModel.class.getDeclaredClasses()) {
+            cnt++;
+            int dolar = c.getName().lastIndexOf('$');
+            assertNotEquals(dolar, -1, "There is dolar in : " + c.getName());
+            String res = c.getName().substring(dolar + 1);
+            try {
+                int number = Integer.parseInt(res);
+                if (number == 1) {
+                    // one is OK, #2 was a problem
+                    continue;
+                }
+                fail("There seems to annonymous innerclass! " + c.getName() + "\nImplements: " 
+                    + Arrays.toString(c.getInterfaces()) + " extends: " + c.getSuperclass()
+                );
+            } catch (NumberFormatException ex) {
+                // OK, go on
+            }
+        }
+        if (cnt == 0) {
+            fail("There should be at least one inner class: " + cnt);
+        }
+    }
+    
+    @Test public void copyOperation() {
+        OpModel orig = new OpModel("Ahoj", "Jardo");
+        OpModel n = new OpModel();
+        n.copy(orig);
+        assertEquals(n.getNames().size(), 2, "Two elems");
+        assertEquals(n.getNames().get(0), "Ahoj", "1st");
+        assertEquals(n.getNames().get(1), "Jardo", "2nd");
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/net/java/html/json/PersonImpl.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/net/java/html/json/PersonImpl.java b/json/src/test/java/net/java/html/json/PersonImpl.java
new file mode 100644
index 0000000..581bb6a
--- /dev/null
+++ b/json/src/test/java/net/java/html/json/PersonImpl.java
@@ -0,0 +1,135 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+@Model(className = "Person", properties = {
+    @Property(name = "firstName", type = String.class),
+    @Property(name = "lastName", type = String.class),
+    @Property(name = "sex", type = Sex.class)
+})
+final class PersonImpl {
+    @ComputedProperty 
+    public static String fullName(String firstName, String lastName) {
+        return firstName + " " + lastName;
+    }
+    
+    @ComputedProperty
+    public static List<String> bothNames(String firstName, String lastName) {
+        return Arrays.asList(firstName, lastName);
+    }
+    
+    @ComputedProperty
+    public static String sexType(Sex sex) {
+        return sex == null ? "unknown" : sex.toString();
+    }
+    
+    @ComputedProperty static Sex attractedBy(Sex sex) {
+        if (sex == null) {
+            return null;
+        }
+        return sex == Sex.MALE ? Sex.FEMALE : Sex.MALE;
+    }
+    
+    @Function
+    static void changeSex(Person p, String data) {
+        if (data != null) {
+            p.setSex(Sex.valueOf(data));
+            return;
+        }
+        if (p.getSex() == Sex.MALE) {
+            p.setSex(Sex.FEMALE);
+        } else {
+            p.setSex(Sex.MALE);
+        }
+    }
+    
+    @Model(className = "People", instance = true, targetId="myPeople", properties = {
+        @Property(array = true, name = "info", type = Person.class),
+        @Property(array = true, name = "nicknames", type = String.class),
+        @Property(array = true, name = "age", type = int.class),
+        @Property(array = true, name = "sex", type = Sex.class)
+    })
+    public static class PeopleImpl {
+        private int addAgeCount;
+        private Runnable onInfoChange;
+        
+        @ModelOperation void onInfoChange(People self, Runnable r) {
+            onInfoChange = r;
+        }
+        
+        @ModelOperation void addAge42(People p) {
+            p.getAge().add(42);
+            addAgeCount++;
+        }
+
+        @OnReceive(url = "url", method = "WebSocket", data = String.class)
+        void innerClass(People p, String d) {
+        }
+        
+        @Function void inInnerClass(People p, Person data, int x, double y, String nick) throws IOException {
+            p.getInfo().add(data);
+            p.getAge().add(x);
+            p.getAge().add((int)y);
+            p.getNicknames().add(nick);
+        }
+        
+        @ModelOperation void readAddAgeCount(People p, int[] holder, Runnable whenDone) {
+            holder[0] = addAgeCount;
+            whenDone.run();
+        }
+        
+        @OnPropertyChange("age") void infoChange(People p) {
+            if (onInfoChange != null) {
+                onInfoChange.run();
+            }
+        }
+    }
+}