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:44 UTC

[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

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));
+    }
+}