json/src/main/java/org/netbeans/html/json/spi/Proto.java
changeset 838 bdc3d696dd4a
parent 835 14fcf4844fad
child 908 ee7a0b3b2d4c
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/json/src/main/java/org/netbeans/html/json/spi/Proto.java	Tue Aug 26 18:13:30 2014 +0200
     1.3 @@ -0,0 +1,843 @@
     1.4 +/**
     1.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     1.6 + *
     1.7 + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
     1.8 + *
     1.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    1.10 + * Other names may be trademarks of their respective owners.
    1.11 + *
    1.12 + * The contents of this file are subject to the terms of either the GNU
    1.13 + * General Public License Version 2 only ("GPL") or the Common
    1.14 + * Development and Distribution License("CDDL") (collectively, the
    1.15 + * "License"). You may not use this file except in compliance with the
    1.16 + * License. You can obtain a copy of the License at
    1.17 + * http://www.netbeans.org/cddl-gplv2.html
    1.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    1.19 + * specific language governing permissions and limitations under the
    1.20 + * License.  When distributing the software, include this License Header
    1.21 + * Notice in each file and include the License file at
    1.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    1.23 + * particular file as subject to the "Classpath" exception as provided
    1.24 + * by Oracle in the GPL Version 2 section of the License file that
    1.25 + * accompanied this code. If applicable, add the following below the
    1.26 + * License Header, with the fields enclosed by brackets [] replaced by
    1.27 + * your own identifying information:
    1.28 + * "Portions Copyrighted [year] [name of copyright owner]"
    1.29 + *
    1.30 + * Contributor(s):
    1.31 + *
    1.32 + * The Original Software is NetBeans. The Initial Developer of the Original
    1.33 + * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
    1.34 + *
    1.35 + * If you wish your version of this file to be governed by only the CDDL
    1.36 + * or only the GPL Version 2, indicate your decision by adding
    1.37 + * "[Contributor] elects to include this software in this distribution
    1.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
    1.39 + * single choice of license, a recipient has the option to distribute
    1.40 + * your version of this file under either the CDDL, the GPL Version 2 or
    1.41 + * to extend the choice of license to its licensees as provided above.
    1.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
    1.43 + * Version 2 license, then the option applies only if the new code is
    1.44 + * made subject to such option by the copyright holder.
    1.45 + */
    1.46 +package org.netbeans.html.json.spi;
    1.47 +
    1.48 +import java.util.Collection;
    1.49 +import java.util.List;
    1.50 +import net.java.html.BrwsrCtx;
    1.51 +import net.java.html.json.ComputedProperty;
    1.52 +import net.java.html.json.Model;
    1.53 +import org.netbeans.html.json.impl.Bindings;
    1.54 +import org.netbeans.html.json.impl.JSON;
    1.55 +import org.netbeans.html.json.impl.JSONList;
    1.56 +import org.netbeans.html.json.impl.RcvrJSON;
    1.57 +import org.netbeans.html.json.impl.RcvrJSON.MsgEvnt;
    1.58 +
    1.59 +/** Object associated with one instance of a model generated by the
    1.60 + * {@link Model} annotation. Contains methods the generated class can
    1.61 + * use to communicate with behind the scene associated {@link Technology}.
    1.62 + * Each {@link Proto} object is associated with <a href="http://wiki.apidesign.org/wiki/Singletonizer">
    1.63 + * singletonizer</a>-like interface {@link Type} which provides the 
    1.64 + * associated {@link Technology} the necessary information about the 
    1.65 + * generated {@link Model} class.
    1.66 + *
    1.67 + * @author Jaroslav Tulach
    1.68 + * @since 0.7
    1.69 + */
    1.70 +public final class Proto {
    1.71 +    private final Object obj;
    1.72 +    private final Type type;
    1.73 +    private final net.java.html.BrwsrCtx context;
    1.74 +    private org.netbeans.html.json.impl.Bindings ko;
    1.75 +    private Observers observers;
    1.76 +
    1.77 +    Proto(Object obj, Type type, BrwsrCtx context) {
    1.78 +        this.obj = obj;
    1.79 +        this.type = type;
    1.80 +        this.context = context;
    1.81 +    }
    1.82 +
    1.83 +    /** Browser context this proto object and its associated model
    1.84 +     * are operating-in.
    1.85 +     * 
    1.86 +     * @return the associated context 
    1.87 +     */
    1.88 +    public BrwsrCtx getContext() {
    1.89 +        return context;
    1.90 +    }
    1.91 +
    1.92 +    /** Acquires global lock to compute a {@link ComputedProperty derived property}
    1.93 +     * on this proto object. This proto object must not be locked yet. No
    1.94 +     * dependency tracking is performed.
    1.95 +     * 
    1.96 +     * @throws IllegalStateException if already locked
    1.97 +     */
    1.98 +    public void acquireLock() throws IllegalStateException {
    1.99 +        acquireLock(null);
   1.100 +    }
   1.101 +    
   1.102 +    /** Acquires global lock to compute a {@link ComputedProperty derived property}
   1.103 +     * on this proto object. This proto object must not be locked yet. The
   1.104 +     * name of the property is used to track dependencies on own
   1.105 +     * properties of other proto objects - when they are changed, this
   1.106 +     * {@link #valueHasMutated(java.lang.String) property is changed too}.
   1.107 +     * 
   1.108 +     * @param propName name of property we are about to compute
   1.109 +     * @throws IllegalStateException thrown when there is a cyclic
   1.110 +     *   call is detected
   1.111 +     * @since 0.9
   1.112 +     */
   1.113 +    public void acquireLock(String propName) throws IllegalStateException {
   1.114 +        Observers.beginComputing(this, propName);
   1.115 +    }
   1.116 +    
   1.117 +    /** A property on this proto object is about to be accessed. Verifies
   1.118 +     * whether this proto object is accessible - e.g. it has not been
   1.119 +     * {@link #acquireLock() locked yet}. If everything is OK, the
   1.120 +     * <code>propName</code> is recorded in the chain of dependencies
   1.121 +     * tracked by {@link #acquireLock(java.lang.String)} and watched by
   1.122 +     * {@link #valueHasMutated(java.lang.String)}.
   1.123 +     * 
   1.124 +     * @param propName name of the property that is requested
   1.125 +     * @throws IllegalStateException if the model is locked
   1.126 +     * @since 0.9
   1.127 +     */
   1.128 +    public void accessProperty(String propName) throws IllegalStateException {
   1.129 +        Observers.accessingValue(this, propName);
   1.130 +    }
   1.131 +    
   1.132 +    /** Verifies the model is not locked otherwise throws an exception.
   1.133 +     * @throws IllegalStateException if the model is locked
   1.134 +     */
   1.135 +    public void verifyUnlocked() throws IllegalStateException {
   1.136 +        Observers.verifyUnlocked(this);
   1.137 +    }
   1.138 +    
   1.139 +    /** When modifications are over, the model is switched into 
   1.140 +     * unlocked state by calling this method.
   1.141 +     */
   1.142 +    public void releaseLock() {
   1.143 +        Observers.finishComputing(this);
   1.144 +    }
   1.145 +    
   1.146 +    /** Whenever model changes a property. It should notify the
   1.147 +     * associated technology by calling this method. 
   1.148 +     * Since 0.8.3: This method may be called by any thread - it reschedules
   1.149 +     * its actual execution into appropriate one by using
   1.150 +     * {@link BrwsrCtx#execute(java.lang.Runnable)}.
   1.151 +     * 
   1.152 +     * @param propName name of the changed property
   1.153 +     */
   1.154 +    public void valueHasMutated(final String propName) {
   1.155 +        context.execute(new Runnable() {
   1.156 +            @Override
   1.157 +            public void run() {
   1.158 +                if (ko != null) {
   1.159 +                    ko.valueHasMutated(propName, null, null);
   1.160 +                }
   1.161 +                Observers.valueHasMutated(Proto.this, propName);
   1.162 +            }
   1.163 +        });
   1.164 +    }
   1.165 +
   1.166 +    /** Whenever model changes a propertyit should notify the
   1.167 +     * associated technology. Either by calling this method
   1.168 +     * (if the new value is known and different to the old one) or
   1.169 +     * via (slightly ineffective) {@link #valueHasMutated(java.lang.String)}
   1.170 +     * method.
   1.171 +     * Since 0.8.3: This method may be called by any thread - it reschedules
   1.172 +     * its actual execution into appropriate one by using
   1.173 +     * {@link BrwsrCtx#execute(java.lang.Runnable)}.
   1.174 +     * 
   1.175 +     * @param propName name of the changed property
   1.176 +     * @param oldValue provides previous value of the property
   1.177 +     * @param newValue provides new value of the property
   1.178 +     * @since 0.7.6
   1.179 +     */
   1.180 +    public void valueHasMutated(
   1.181 +        final String propName, final Object oldValue, final Object newValue
   1.182 +    ) {
   1.183 +        context.execute(new Runnable() {
   1.184 +            @Override
   1.185 +            public void run() {
   1.186 +                if (ko != null) {
   1.187 +                    ko.valueHasMutated(propName, oldValue, newValue);
   1.188 +                }
   1.189 +                Observers.valueHasMutated(Proto.this, propName);
   1.190 +            }
   1.191 +        });
   1.192 +    }
   1.193 +    
   1.194 +    /** Initializes the associated model in the current {@link #getContext() context}.
   1.195 +     * In case of <em>knockout.js</em> technology, applies given bindings 
   1.196 +     * of the current model to the <em>body</em> element of the page.
   1.197 +     */
   1.198 +    public void applyBindings() {
   1.199 +        initBindings().applyBindings();
   1.200 +    }
   1.201 +    
   1.202 +    /** Invokes the provided runnable in the {@link #getContext() context}
   1.203 +     * of the browser. If the caller is already on the right thread, the
   1.204 +     * <code>run.run()</code> is invoked immediately and synchronously. 
   1.205 +     * Otherwise the method returns immediately and the <code>run()</code>
   1.206 +     * method is performed later
   1.207 +     * 
   1.208 +     * @param run the action to execute
   1.209 +     */
   1.210 +    public void runInBrowser(Runnable run) {
   1.211 +        context.execute(run);
   1.212 +    }
   1.213 +
   1.214 +    /** Invokes the specified function index in the {@link #getContext() context}
   1.215 +     * of the browser. If the caller is already on the right thread, the
   1.216 +     * index-th function is invoked immediately and synchronously. 
   1.217 +     * Otherwise the method returns immediately and the function is invoked
   1.218 +     * later.
   1.219 +     * 
   1.220 +     * @param index the index of the function as will be passed to
   1.221 +     *   {@link Type#call(java.lang.Object, int, java.lang.Object, java.lang.Object)}
   1.222 +     *   method
   1.223 +     * @param args array of arguments that will be passed as
   1.224 +     *   <code>data</code> argument of the <code>call</code> method.
   1.225 +     * @since 0.7.6
   1.226 +     */
   1.227 +    public void runInBrowser(final int index, final Object... args) {
   1.228 +        context.execute(new Runnable() {
   1.229 +            @Override
   1.230 +            public void run() {
   1.231 +                try {
   1.232 +                    type.call(obj, index, args, null);
   1.233 +                } catch (Exception ex) {
   1.234 +                    ex.printStackTrace();
   1.235 +                }
   1.236 +            }
   1.237 +        });
   1.238 +    }
   1.239 +    
   1.240 +    /** Initializes the provided collection with a content of the <code>array</code>.
   1.241 +     * The initialization can only be done soon after the the collection 
   1.242 +     * is created, otherwise an exception is throw
   1.243 +     * 
   1.244 +     * @param to the collection to initialize (assumed to be empty)
   1.245 +     * @param array the array to add to the collection
   1.246 +     * @throws IllegalStateException if the system has already been initialized
   1.247 +     */
   1.248 +    public void initTo(Collection<?> to, Object array) {
   1.249 +        if (ko != null) {
   1.250 +            throw new IllegalStateException();
   1.251 +        }
   1.252 +        if (to instanceof JSONList) {
   1.253 +           ((JSONList)to).init(array);
   1.254 +        } else {
   1.255 +            JSONList.init(to, array);
   1.256 +        }
   1.257 +    }
   1.258 +
   1.259 +    /** Takes an object representing JSON result and extract some of its
   1.260 +     * properties. It is assumed that the <code>props</code> and
   1.261 +     * <code>values</code> arrays have the same length.
   1.262 +     * 
   1.263 +     * @param json the JSON object (actual type depends on the associated
   1.264 +     *   {@link Technology})
   1.265 +     * @param props list of properties to extract
   1.266 +     * @param values array that will be filled with extracted values
   1.267 +     */
   1.268 +    public void extract(Object json, String[] props, Object[] values) {
   1.269 +        JSON.extract(context, json, props, values);
   1.270 +    }
   1.271 +
   1.272 +    /** Converts raw JSON <code>data</code> into a Java {@link Model} class.
   1.273 +     * 
   1.274 +     * @param <T> type of the model class
   1.275 +     * @param modelClass the type of the class to create
   1.276 +     * @param data the raw JSON data
   1.277 +     * @return newly created instance of the model class
   1.278 +     */
   1.279 +    public <T> T read(Class<T> modelClass, Object data) {
   1.280 +        return JSON.read(context, modelClass, data);
   1.281 +    }
   1.282 +
   1.283 +    /** Initializes asynchronous JSON connection to specified URL. Delegates
   1.284 +     * to {@link #loadJSON(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object, java.lang.Object...) }
   1.285 +     * with no extra parameters.
   1.286 +     * 
   1.287 +     * @param index the callback index to be used when a reply is received
   1.288 +     *   to call {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object)}.
   1.289 +     * 
   1.290 +     * @param urlBefore the part of the URL before JSON-P callback parameter
   1.291 +     * @param urlAfter the rest of the URL or <code>null</code> if no JSON-P is used
   1.292 +     * @param method method to use for connection to the server
   1.293 +     * @param data string, number or a {@link Model} generated class to send to
   1.294 +     *    the server when doing a query
   1.295 +     */
   1.296 +    public void loadJSON(final int index, 
   1.297 +        String urlBefore, String urlAfter, String method,
   1.298 +        final Object data
   1.299 +    ) {
   1.300 +        loadJSON(index, urlBefore, urlAfter, method, data, new Object[0]);
   1.301 +    }
   1.302 +    
   1.303 +    /** Initializes asynchronous JSON connection to specified URL. The 
   1.304 +     * method returns immediately and later does callback later.
   1.305 +     * 
   1.306 +     * @param index the callback index to be used when a reply is received
   1.307 +     *   to call {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object)}.
   1.308 +     * 
   1.309 +     * @param urlBefore the part of the URL before JSON-P callback parameter
   1.310 +     * @param urlAfter the rest of the URL or <code>null</code> if no JSON-P is used
   1.311 +     * @param method method to use for connection to the server
   1.312 +     * @param data string, number or a {@link Model} generated class to send to
   1.313 +     *    the server when doing a query
   1.314 +     * @param params extra params to pass back when calling
   1.315 +     *   {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object, java.lang.Object[])}
   1.316 +     * @since 0.8.1
   1.317 +     */
   1.318 +    public void loadJSON(final int index, 
   1.319 +        String urlBefore, String urlAfter, String method,
   1.320 +        final Object data, final Object... params
   1.321 +    ) {
   1.322 +        class Rcvr extends RcvrJSON {
   1.323 +            @Override
   1.324 +            protected void onMessage(MsgEvnt msg) {
   1.325 +                type.onMessage(obj, index, 1, msg.getValues(), params);
   1.326 +            }
   1.327 +
   1.328 +            @Override
   1.329 +            protected void onError(MsgEvnt msg) {
   1.330 +                type.onMessage(obj, index, 2, msg.getException(), params);
   1.331 +            }
   1.332 +        }
   1.333 +        JSON.loadJSON(context, new Rcvr(), urlBefore, urlAfter, method, data);
   1.334 +    }
   1.335 +    
   1.336 +    /** Opens new WebSocket connection to the specified URL. 
   1.337 +     * 
   1.338 +     * @param index the index to use later during callbacks to 
   1.339 +     *   {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object)}
   1.340 +     * @param url the <code>ws://</code> or <code>wss://</code> URL to connect to
   1.341 +     * @param data data to send to server (usually <code>null</code>)
   1.342 +     * @return returns a non-null object representing the socket
   1.343 +     *   which can be used when calling {@link #wsSend(java.lang.Object, java.lang.String, java.lang.Object) }
   1.344 +     */
   1.345 +    public Object wsOpen(final int index, String url, Object data) {
   1.346 +        class WSrcvr extends RcvrJSON {
   1.347 +            @Override
   1.348 +            protected void onError(MsgEvnt msg) {
   1.349 +                type.onMessage(obj, index, 2, msg.getException());
   1.350 +            }
   1.351 +            
   1.352 +            @Override
   1.353 +            protected void onMessage(MsgEvnt msg) {
   1.354 +                type.onMessage(obj, index, 1, msg.getValues());
   1.355 +            }
   1.356 +            
   1.357 +            @Override
   1.358 +            protected void onClose(MsgEvnt msg) {
   1.359 +                type.onMessage(obj, index, 3, null);
   1.360 +            }
   1.361 +
   1.362 +            @Override
   1.363 +            protected void onOpen(MsgEvnt msg) {
   1.364 +                type.onMessage(obj, index, 0, null);
   1.365 +            }
   1.366 +        }
   1.367 +        return JSON.openWS(context, new WSrcvr(), url, data);
   1.368 +    }
   1.369 +    
   1.370 +    /** Sends a message to existing socket.
   1.371 +     * 
   1.372 +     * @param webSocket the socket to send message to
   1.373 +     * @param url the <code>ws://</code> or <code>wss://</code> URL to connect to,
   1.374 +     *    preferably the same as the one used when the socket was 
   1.375 +     *    {@link #wsOpen(int, java.lang.String, java.lang.Object) opened}
   1.376 +     * @param data the data to send or <code>null</code> if the socket is
   1.377 +     *    supposed to be closed
   1.378 +     */
   1.379 +    public void wsSend(Object webSocket, String url, Object data) {
   1.380 +        ((JSON.WS)webSocket).send(context, url, data);
   1.381 +    }
   1.382 +
   1.383 +    /** Converts raw data (one of its properties) to string representation.
   1.384 +     * 
   1.385 +     * @param data the object
   1.386 +     * @param propName the name of object property or <code>null</code>
   1.387 +     *   if the whole object should be converted
   1.388 +     * @return the string representation of the object or its property
   1.389 +     */
   1.390 +    public String toString(Object data, String propName) {
   1.391 +        return JSON.toString(context, data, propName);
   1.392 +    }
   1.393 +    
   1.394 +    /** Converts raw data (one of its properties) to a number representation.
   1.395 +     * 
   1.396 +     * @param data the object
   1.397 +     * @param propName the name of object property or <code>null</code>
   1.398 +     *   if the whole object should be converted
   1.399 +     * @return the number representation of the object or its property
   1.400 +     */
   1.401 +    public Number toNumber(Object data, String propName) {
   1.402 +        return JSON.toNumber(context, data, propName);
   1.403 +    }
   1.404 +
   1.405 +    /** Converts raw JSON data into a {@link Model} class representation.
   1.406 +     * 
   1.407 +     * @param <T> type of the model to create
   1.408 +     * @param type class of the model to create
   1.409 +     * @param data raw JSON data (depends on associated {@link Technology})
   1.410 +     * @return new instances of the model class filled with values from the
   1.411 +     *   <code>data</code> object
   1.412 +     */
   1.413 +    public <T> T toModel(Class<T> type, Object data) {
   1.414 +        return JSON.toModel(context, type, data, null);
   1.415 +    }
   1.416 +
   1.417 +    /** Creates new JSON like observable list.
   1.418 +     * 
   1.419 +     * @param <T> the type of the list elements
   1.420 +     * @param propName name of a property this list is associated with
   1.421 +     * @param onChange index of the property to use when the list is modified
   1.422 +     *   during callback to {@link Type#onChange(java.lang.Object, int)}
   1.423 +     * @param dependingProps the array of {@link ComputedProperty derived properties}
   1.424 +     *   that depend on the value of the list
   1.425 +     * @return new, empty list associated with this proto-object and its model
   1.426 +     */
   1.427 +    public <T> List<T> createList(String propName, int onChange, String... dependingProps) {
   1.428 +        return new JSONList<T>(this, propName, onChange, dependingProps);
   1.429 +    }
   1.430 +
   1.431 +    /** Copies content of one collection to another, re-assigning all its
   1.432 +     * elements from their current context to the new <code>ctx</code>.
   1.433 +     * 
   1.434 +     * @param <T> type of the collections
   1.435 +     * @param to the target collection to be filled with cloned values
   1.436 +     * @param ctx context for the new collection
   1.437 +     * @param from original collection with its data
   1.438 +     */
   1.439 +    public <T> void cloneList(Collection<T> to, BrwsrCtx ctx, Collection<T> from) {
   1.440 +        Boolean isModel = null;
   1.441 +        for (T t : from) {
   1.442 +            if (isModel == null) {
   1.443 +                isModel = JSON.isModel(t.getClass());
   1.444 +            }
   1.445 +            if (isModel) {
   1.446 +                to.add(JSON.bindTo(t, ctx));
   1.447 +            } else {
   1.448 +                to.add(t);
   1.449 +            }
   1.450 +        }
   1.451 +    }
   1.452 +    
   1.453 +    //
   1.454 +    // internal state
   1.455 +    //
   1.456 +    
   1.457 +    final String toStr() {
   1.458 +        return "Proto[" + obj + "]@" + Integer.toHexString(System.identityHashCode(this));
   1.459 +    }
   1.460 +    
   1.461 +    final Bindings initBindings() {
   1.462 +        if (ko == null) {
   1.463 +            Bindings b = Bindings.apply(context, obj);
   1.464 +            PropertyBinding[] pb = new PropertyBinding[type.propertyNames.length];
   1.465 +            for (int i = 0; i < pb.length; i++) {
   1.466 +                pb[i] = b.registerProperty(
   1.467 +                    type.propertyNames[i], i, obj, type, type.propertyReadOnly[i]
   1.468 +                );
   1.469 +            }
   1.470 +            FunctionBinding[] fb = new FunctionBinding[type.functions.length];
   1.471 +            for (int i = 0; i < fb.length; i++) {
   1.472 +                fb[i] = FunctionBinding.registerFunction(
   1.473 +                    type.functions[i], i, obj, type
   1.474 +                );
   1.475 +            }
   1.476 +            ko = b;
   1.477 +            b.finish(obj, pb, fb);
   1.478 +        }
   1.479 +        return ko;
   1.480 +    }
   1.481 +
   1.482 +    final Bindings getBindings() {
   1.483 +        return ko;
   1.484 +    }
   1.485 +
   1.486 +    final void onChange(int index) {
   1.487 +        type.onChange(obj, index);
   1.488 +    }
   1.489 +
   1.490 +    final Observers observers(boolean create) {
   1.491 +        if (create && observers == null) {
   1.492 +            observers = new Observers();
   1.493 +        }
   1.494 +        return observers;
   1.495 +    }
   1.496 +
   1.497 +    /** Functionality used by the code generated by annotation
   1.498 +     * processor for the {@link net.java.html.json.Model} annotation.
   1.499 +     * 
   1.500 +     * @param <Model> the generated class
   1.501 +     * @since 0.7
   1.502 +     */
   1.503 +    public static abstract class Type<Model> {
   1.504 +        private final Class<Model> clazz;
   1.505 +        private final String[] propertyNames;
   1.506 +        private final boolean[] propertyReadOnly;
   1.507 +        private final String[] functions;
   1.508 +
   1.509 +        /** Constructor for subclasses generated by the annotation processor
   1.510 +         * associated with {@link net.java.html.json.Model} annotation.
   1.511 +         * 
   1.512 +         * @param clazz the generated model class
   1.513 +         * @param modelFor the original class annotated by the {@link net.java.html.json.Model} annotation.
   1.514 +         * @param properties number of properties the class has
   1.515 +         * @param functions  number of functions the class has
   1.516 +         */
   1.517 +        protected Type(
   1.518 +            Class<Model> clazz, Class<?> modelFor, int properties, int functions
   1.519 +        ) {
   1.520 +            assert getClass().getName().endsWith("$Html4JavaType");
   1.521 +            try {
   1.522 +                assert getClass().getDeclaringClass() == clazz;
   1.523 +            } catch (SecurityException ex) {
   1.524 +                // OK, no check
   1.525 +            }
   1.526 +            this.clazz = clazz;
   1.527 +            this.propertyNames = new String[properties];
   1.528 +            this.propertyReadOnly = new boolean[properties];
   1.529 +            this.functions = new String[functions];
   1.530 +            JSON.register(clazz, this);
   1.531 +        }
   1.532 +
   1.533 +        /** Registers property for the type. It is expected each index
   1.534 +         * is initialized only once.
   1.535 +         * 
   1.536 +         * @param name name of the property
   1.537 +         * @param index index of the property
   1.538 +         * @param readOnly is the property read only?
   1.539 +         */
   1.540 +        protected final void registerProperty(String name, int index, boolean readOnly) {
   1.541 +            assert propertyNames[index] == null;
   1.542 +            propertyNames[index] = name;
   1.543 +            propertyReadOnly[index] = readOnly;
   1.544 +        }
   1.545 +
   1.546 +        /** Registers function of given name at given index.
   1.547 +         * 
   1.548 +         * @param name name of the function
   1.549 +         * @param index name of the type
   1.550 +         */
   1.551 +        protected final void registerFunction(String name, int index) {
   1.552 +            assert functions[index] == null;
   1.553 +            functions[index] = name;
   1.554 +        }
   1.555 +        
   1.556 +        /** Creates new proto-object for given {@link Model} class bound to
   1.557 +         * provided context.
   1.558 +         * 
   1.559 +         * @param obj instance of appropriate {@link Model} class
   1.560 +         * @param context the browser context
   1.561 +         * @return new proto-object that the generated class can use for
   1.562 +         *   communication with the infrastructure
   1.563 +         */
   1.564 +        public Proto createProto(Object obj, BrwsrCtx context) {
   1.565 +            return new Proto(obj, this, context);
   1.566 +        }
   1.567 +        
   1.568 +        //
   1.569 +        // Implemented by subclasses
   1.570 +        //
   1.571 +        
   1.572 +        /** Sets value of a {@link #registerProperty(java.lang.String, int, boolean) registered property}
   1.573 +         * to new value.
   1.574 +         * 
   1.575 +         * @param model the instance of {@link Model model class}
   1.576 +         * @param index index of the property used during registration
   1.577 +         * @param value the value to set the property to
   1.578 +         */
   1.579 +        protected abstract void setValue(Model model, int index, Object value);
   1.580 +        
   1.581 +        /** Obtains and returns value of a 
   1.582 +         * {@link #registerProperty(java.lang.String, int, boolean) registered property}.
   1.583 +         * 
   1.584 +         * @param model the instance of {@link Model model class}
   1.585 +         * @param index index of the property used during registration
   1.586 +         * @return current value of the property
   1.587 +         */
   1.588 +        protected abstract Object getValue(Model model, int index);
   1.589 +        
   1.590 +        /** Invokes a {@link #registerFunction(java.lang.String, int)} registered function
   1.591 +         * on given object.
   1.592 +         * 
   1.593 +         * @param model the instance of {@link Model model class}
   1.594 +         * @param index index of the property used during registration
   1.595 +         * @param data the currently selected object the function is about to operate on
   1.596 +         * @param event the event that triggered the event
   1.597 +         * @throws Exception the method can throw exception which is then logged
   1.598 +         */
   1.599 +        protected abstract void call(Model model, int index, Object data, Object event)
   1.600 +        throws Exception;
   1.601 +        
   1.602 +        /** Re-binds the model object to new browser context.
   1.603 +         * 
   1.604 +         * @param model the instance of {@link Model model class}
   1.605 +         * @param ctx browser context to clone the object to
   1.606 +         * @return new instance of the model suitable for new context
   1.607 +         */
   1.608 +        protected abstract Model cloneTo(Model model, BrwsrCtx ctx);
   1.609 +        
   1.610 +        /** Reads raw JSON data and converts them to our model class.
   1.611 +         * 
   1.612 +         * @param c the browser context to work in
   1.613 +         * @param json raw JSON data to get values from
   1.614 +         * @return new instance of model class filled by the data
   1.615 +         */
   1.616 +        protected abstract Model read(BrwsrCtx c, Object json);
   1.617 +        
   1.618 +        /** Called when a {@link #registerProperty(java.lang.String, int, boolean) registered property}
   1.619 +         * changes its value.
   1.620 +         * 
   1.621 +         * @param model the object that has the property
   1.622 +         * @param index the index of the property during registration
   1.623 +         */
   1.624 +        protected abstract void onChange(Model model, int index);
   1.625 +        
   1.626 +        /** Finds out if there is an associated proto-object for given
   1.627 +         * object.
   1.628 +         * 
   1.629 +         * @param object an object, presumably (but not necessarily) instance of Model class
   1.630 +         * @return associated proto-object or <code>null</code>
   1.631 +         */
   1.632 +        protected abstract Proto protoFor(Object object);
   1.633 +
   1.634 +        /** Called to report results of asynchronous over-the-wire 
   1.635 +         * communication. Result of calling {@link Proto#wsOpen(int, java.lang.String, java.lang.Object)}
   1.636 +         * or {@link Proto#loadJSON(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object, java.lang.Object...)}.
   1.637 +         * 
   1.638 +         * @param model the instance of the model class
   1.639 +         * @param index index used during initiating the communication (via <code>loadJSON</code> or <code>wsOpen</code> calls)
   1.640 +         * @param type type of the message: 0 - onOpen, 1 - onMessage, 2 - onError, 3 - onClose -
   1.641 +         *   not all messages are applicable to all communication protocols (JSON has only 1 and 2).
   1.642 +         * @param data <code>null</code> or string, number or a {@link Model} class
   1.643 +         *   obtained to the server as a response
   1.644 +         */
   1.645 +        protected void onMessage(Model model, int index, int type, Object data) {
   1.646 +            onMessage(model, index, type, data, new Object[0]);
   1.647 +        }
   1.648 +        
   1.649 +        /** Called to report results of asynchronous over-the-wire 
   1.650 +         * communication. Result of calling {@link Proto#wsOpen(int, java.lang.String, java.lang.Object)}
   1.651 +         * or {@link Proto#loadJSON(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object, java.lang.Object...)}.
   1.652 +         * 
   1.653 +         * @param model the instance of the model class
   1.654 +         * @param index index used during initiating the communication (via <code>loadJSON</code> or <code>wsOpen</code> calls)
   1.655 +         * @param type type of the message: 0 - onOpen, 1 - onMessage, 2 - onError, 3 - onClose -
   1.656 +         *   not all messages are applicable to all communication protocols (JSON has only 1 and 2).
   1.657 +         * @param data <code>null</code> or string, number or a {@link Model} class
   1.658 +         *   obtained to the server as a response
   1.659 +         * @param params extra parameters as passed for example to
   1.660 +         *   {@link Proto#loadJSON(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object, java.lang.Object...)}
   1.661 +         *   method
   1.662 +         * @since 0.8.1
   1.663 +         */
   1.664 +        protected void onMessage(Model model, int index, int type, Object data, Object[] params) {
   1.665 +            onMessage(model, index, type, data);
   1.666 +        }
   1.667 +
   1.668 +        //
   1.669 +        // Various support methods the generated classes use
   1.670 +        //
   1.671 +
   1.672 +        /** Converts and array of raw JSON objects into an array of typed
   1.673 +         * Java {@link Model} classes.
   1.674 +         * 
   1.675 +         * @param <T> the type of the destination array
   1.676 +         * @param context browser context to use
   1.677 +         * @param src array of raw JSON objects
   1.678 +         * @param destType type of the individual array elements
   1.679 +         * @param dest array to be filled with read type instances
   1.680 +         */
   1.681 +        public <T> void copyJSON(BrwsrCtx context, Object[] src, Class<T> destType, T[] dest) {
   1.682 +            for (int i = 0; i < src.length && i < dest.length; i++) {
   1.683 +                dest[i] = org.netbeans.html.json.impl.JSON.read(context, destType, src[i]);
   1.684 +            }
   1.685 +        }
   1.686 +        
   1.687 +        /** Compares two objects that can be converted to integers.
   1.688 +         * @param a first value
   1.689 +         * @param b second value
   1.690 +         * @return true if they are the same
   1.691 +         */
   1.692 +        public final boolean isSame(int a, int b) {
   1.693 +            return a == b;
   1.694 +        }
   1.695 +
   1.696 +        /** Compares two objects that can be converted to (floating point)
   1.697 +         * numbers.
   1.698 +         * @param a first value
   1.699 +         * @param b second value
   1.700 +         * @return  true if they are the same
   1.701 +         */
   1.702 +        public final boolean isSame(double a, double b) {
   1.703 +            return a == b;
   1.704 +        }
   1.705 +
   1.706 +        /** Compares two objects for being the same - e.g. either <code>==</code>
   1.707 +         * or <code>equals</code>.
   1.708 +         * @param a first value
   1.709 +         * @param b second value
   1.710 +         * @return true if they are equals
   1.711 +         */ 
   1.712 +        public final boolean isSame(Object a, Object b) {
   1.713 +            if (a == b) {
   1.714 +                return true;
   1.715 +            }
   1.716 +            if (a == null || b == null) {
   1.717 +                return false;
   1.718 +            }
   1.719 +            return a.equals(b);
   1.720 +        }
   1.721 +
   1.722 +        /** Cumulative hash function. Adds hashcode of the object to the
   1.723 +         * previous value.
   1.724 +         * @param o the object (or <code>null</code>)
   1.725 +         * @param h the previous value of the hash
   1.726 +         * @return new hash - the old one xor the object's one
   1.727 +         */
   1.728 +        public final int hashPlus(Object o, int h) {
   1.729 +            return o == null ? h : h ^ o.hashCode();
   1.730 +        }
   1.731 +        
   1.732 +        /** Converts an object to its JSON value.
   1.733 +         * 
   1.734 +         * @param obj the object to convert
   1.735 +         * @return JSON representation of the object
   1.736 +         */
   1.737 +        public final String toJSON(Object obj) {
   1.738 +            return JSON.toJSON(obj);
   1.739 +        }
   1.740 +        
   1.741 +        /** Converts the value to string.
   1.742 +         * 
   1.743 +         * @param val the value
   1.744 +         * @return the converted value
   1.745 +         */
   1.746 +        public final String stringValue(Object val) {
   1.747 +            return JSON.stringValue(val);
   1.748 +        }
   1.749 +
   1.750 +        /** Converts the value to number.
   1.751 +         * 
   1.752 +         * @param val the value
   1.753 +         * @return the converted value
   1.754 +         */
   1.755 +        public final Number numberValue(Object val) {
   1.756 +            return JSON.numberValue(val);
   1.757 +        }
   1.758 +
   1.759 +        /** Converts the value to character.
   1.760 +         * 
   1.761 +         * @param val the value
   1.762 +         * @return the converted value
   1.763 +         */
   1.764 +        public final Character charValue(Object val) {
   1.765 +            return JSON.charValue(val);
   1.766 +        }
   1.767 +
   1.768 +        /** Converts the value to boolean.
   1.769 +         * 
   1.770 +         * @param val the value
   1.771 +         * @return the converted value
   1.772 +         */
   1.773 +        public final Boolean boolValue(Object val) {
   1.774 +            return JSON.boolValue(val);
   1.775 +        }
   1.776 +        
   1.777 +        /** Extracts value of specific type from given object.
   1.778 +         * 
   1.779 +         * @param <T> the type of object one is interested in
   1.780 +         * @param type the type
   1.781 +         * @param val the object to convert to type
   1.782 +         * @return the converted value
   1.783 +         */
   1.784 +        public final <T> T extractValue(Class<T> type, Object val) {
   1.785 +            if (Number.class.isAssignableFrom(type)) {
   1.786 +                val = numberValue(val);
   1.787 +            }
   1.788 +            if (Boolean.class == type) {
   1.789 +                val = boolValue(val);
   1.790 +            }
   1.791 +            if (String.class == type) {
   1.792 +                val = stringValue(val);
   1.793 +            }
   1.794 +            if (Character.class == type) {
   1.795 +                val = charValue(val);
   1.796 +            }
   1.797 +            if (Integer.class == type) {
   1.798 +                val = val instanceof Number ? ((Number) val).intValue() : 0;
   1.799 +            }
   1.800 +            if (Long.class == type) {
   1.801 +                val = val instanceof Number ? ((Number) val).longValue() : 0;
   1.802 +            }
   1.803 +            if (Short.class == type) {
   1.804 +                val = val instanceof Number ? ((Number) val).shortValue() : 0;
   1.805 +            }
   1.806 +            if (Byte.class == type) {
   1.807 +                val = val instanceof Number ? ((Number) val).byteValue() : 0;
   1.808 +            }
   1.809 +            if (Double.class == type) {
   1.810 +                val = val instanceof Number ? ((Number) val).doubleValue() : Double.NaN;
   1.811 +            }
   1.812 +            if (Float.class == type) {
   1.813 +                val = val instanceof Number ? ((Number) val).floatValue() : Float.NaN;
   1.814 +            }
   1.815 +            if (type.isEnum() && val instanceof String) {
   1.816 +                val = Enum.valueOf(type.asSubclass(Enum.class), (String)val);
   1.817 +            }
   1.818 +            return type.cast(val);
   1.819 +        }
   1.820 +
   1.821 +        /** Special dealing with array &amp; {@link List} values. This method
   1.822 +         * takes the provided collection, empties it and fills it again
   1.823 +         * with values extracted from <code>value</code> (which is supposed
   1.824 +         * to be an array).
   1.825 +         * 
   1.826 +         * @param <T> the type of list elements
   1.827 +         * @param arr collection to fill with elements in value
   1.828 +         * @param type the type of elements in the collection
   1.829 +         * @param value array of elements to put into the collecition. If
   1.830 +         *   value is not an array it is wrapped into array with only element
   1.831 +         * @since 1.0
   1.832 +         */
   1.833 +        public final <T> void replaceValue(Collection<? super T> arr, Class<T> type, Object value) {
   1.834 +            Object[] newArr;
   1.835 +            if (value instanceof Object[]) {
   1.836 +                newArr = (Object[]) value;
   1.837 +            } else {
   1.838 +                newArr = new Object[] { value };
   1.839 +            }
   1.840 +            arr.clear();
   1.841 +            for (Object e : newArr) {
   1.842 +                arr.add(extractValue(type, e));
   1.843 +            }
   1.844 +        }
   1.845 +    }
   1.846 +}