json/src/main/java/org/netbeans/html/json/spi/Proto.java
author Jaroslav Tulach <jtulach@netbeans.org>
Tue, 16 Dec 2014 21:03:16 +0100
branchApplyId
changeset 908 ee7a0b3b2d4c
parent 838 bdc3d696dd4a
child 940 bdec4103bdb2
permissions -rw-r--r--
Providing an ability to apply model to element with certain id
     1 /**
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
     5  *
     6  * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
     7  * Other names may be trademarks of their respective owners.
     8  *
     9  * The contents of this file are subject to the terms of either the GNU
    10  * General Public License Version 2 only ("GPL") or the Common
    11  * Development and Distribution License("CDDL") (collectively, the
    12  * "License"). You may not use this file except in compliance with the
    13  * License. You can obtain a copy of the License at
    14  * http://www.netbeans.org/cddl-gplv2.html
    15  * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    16  * specific language governing permissions and limitations under the
    17  * License.  When distributing the software, include this License Header
    18  * Notice in each file and include the License file at
    19  * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    20  * particular file as subject to the "Classpath" exception as provided
    21  * by Oracle in the GPL Version 2 section of the License file that
    22  * accompanied this code. If applicable, add the following below the
    23  * License Header, with the fields enclosed by brackets [] replaced by
    24  * your own identifying information:
    25  * "Portions Copyrighted [year] [name of copyright owner]"
    26  *
    27  * Contributor(s):
    28  *
    29  * The Original Software is NetBeans. The Initial Developer of the Original
    30  * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
    31  *
    32  * If you wish your version of this file to be governed by only the CDDL
    33  * or only the GPL Version 2, indicate your decision by adding
    34  * "[Contributor] elects to include this software in this distribution
    35  * under the [CDDL or GPL Version 2] license." If you do not indicate a
    36  * single choice of license, a recipient has the option to distribute
    37  * your version of this file under either the CDDL, the GPL Version 2 or
    38  * to extend the choice of license to its licensees as provided above.
    39  * However, if you add GPL Version 2 code and therefore, elected the GPL
    40  * Version 2 license, then the option applies only if the new code is
    41  * made subject to such option by the copyright holder.
    42  */
    43 package org.netbeans.html.json.spi;
    44 
    45 import java.util.Collection;
    46 import java.util.List;
    47 import net.java.html.BrwsrCtx;
    48 import net.java.html.json.ComputedProperty;
    49 import net.java.html.json.Model;
    50 import org.netbeans.html.json.impl.Bindings;
    51 import org.netbeans.html.json.impl.JSON;
    52 import org.netbeans.html.json.impl.JSONList;
    53 import org.netbeans.html.json.impl.RcvrJSON;
    54 import org.netbeans.html.json.impl.RcvrJSON.MsgEvnt;
    55 
    56 /** Object associated with one instance of a model generated by the
    57  * {@link Model} annotation. Contains methods the generated class can
    58  * use to communicate with behind the scene associated {@link Technology}.
    59  * Each {@link Proto} object is associated with <a href="http://wiki.apidesign.org/wiki/Singletonizer">
    60  * singletonizer</a>-like interface {@link Type} which provides the 
    61  * associated {@link Technology} the necessary information about the 
    62  * generated {@link Model} class.
    63  *
    64  * @author Jaroslav Tulach
    65  * @since 0.7
    66  */
    67 public final class Proto {
    68     private final Object obj;
    69     private final Type type;
    70     private final net.java.html.BrwsrCtx context;
    71     private org.netbeans.html.json.impl.Bindings ko;
    72     private Observers observers;
    73 
    74     Proto(Object obj, Type type, BrwsrCtx context) {
    75         this.obj = obj;
    76         this.type = type;
    77         this.context = context;
    78     }
    79 
    80     /** Browser context this proto object and its associated model
    81      * are operating-in.
    82      * 
    83      * @return the associated context 
    84      */
    85     public BrwsrCtx getContext() {
    86         return context;
    87     }
    88 
    89     /** Acquires global lock to compute a {@link ComputedProperty derived property}
    90      * on this proto object. This proto object must not be locked yet. No
    91      * dependency tracking is performed.
    92      * 
    93      * @throws IllegalStateException if already locked
    94      */
    95     public void acquireLock() throws IllegalStateException {
    96         acquireLock(null);
    97     }
    98     
    99     /** Acquires global lock to compute a {@link ComputedProperty derived property}
   100      * on this proto object. This proto object must not be locked yet. The
   101      * name of the property is used to track dependencies on own
   102      * properties of other proto objects - when they are changed, this
   103      * {@link #valueHasMutated(java.lang.String) property is changed too}.
   104      * 
   105      * @param propName name of property we are about to compute
   106      * @throws IllegalStateException thrown when there is a cyclic
   107      *   call is detected
   108      * @since 0.9
   109      */
   110     public void acquireLock(String propName) throws IllegalStateException {
   111         Observers.beginComputing(this, propName);
   112     }
   113     
   114     /** A property on this proto object is about to be accessed. Verifies
   115      * whether this proto object is accessible - e.g. it has not been
   116      * {@link #acquireLock() locked yet}. If everything is OK, the
   117      * <code>propName</code> is recorded in the chain of dependencies
   118      * tracked by {@link #acquireLock(java.lang.String)} and watched by
   119      * {@link #valueHasMutated(java.lang.String)}.
   120      * 
   121      * @param propName name of the property that is requested
   122      * @throws IllegalStateException if the model is locked
   123      * @since 0.9
   124      */
   125     public void accessProperty(String propName) throws IllegalStateException {
   126         Observers.accessingValue(this, propName);
   127     }
   128     
   129     /** Verifies the model is not locked otherwise throws an exception.
   130      * @throws IllegalStateException if the model is locked
   131      */
   132     public void verifyUnlocked() throws IllegalStateException {
   133         Observers.verifyUnlocked(this);
   134     }
   135     
   136     /** When modifications are over, the model is switched into 
   137      * unlocked state by calling this method.
   138      */
   139     public void releaseLock() {
   140         Observers.finishComputing(this);
   141     }
   142     
   143     /** Whenever model changes a property. It should notify the
   144      * associated technology by calling this method. 
   145      * Since 0.8.3: This method may be called by any thread - it reschedules
   146      * its actual execution into appropriate one by using
   147      * {@link BrwsrCtx#execute(java.lang.Runnable)}.
   148      * 
   149      * @param propName name of the changed property
   150      */
   151     public void valueHasMutated(final String propName) {
   152         context.execute(new Runnable() {
   153             @Override
   154             public void run() {
   155                 if (ko != null) {
   156                     ko.valueHasMutated(propName, null, null);
   157                 }
   158                 Observers.valueHasMutated(Proto.this, propName);
   159             }
   160         });
   161     }
   162 
   163     /** Whenever model changes a propertyit should notify the
   164      * associated technology. Either by calling this method
   165      * (if the new value is known and different to the old one) or
   166      * via (slightly ineffective) {@link #valueHasMutated(java.lang.String)}
   167      * method.
   168      * Since 0.8.3: This method may be called by any thread - it reschedules
   169      * its actual execution into appropriate one by using
   170      * {@link BrwsrCtx#execute(java.lang.Runnable)}.
   171      * 
   172      * @param propName name of the changed property
   173      * @param oldValue provides previous value of the property
   174      * @param newValue provides new value of the property
   175      * @since 0.7.6
   176      */
   177     public void valueHasMutated(
   178         final String propName, final Object oldValue, final Object newValue
   179     ) {
   180         context.execute(new Runnable() {
   181             @Override
   182             public void run() {
   183                 if (ko != null) {
   184                     ko.valueHasMutated(propName, oldValue, newValue);
   185                 }
   186                 Observers.valueHasMutated(Proto.this, propName);
   187             }
   188         });
   189     }
   190     
   191     /** Initializes the associated model in the current {@link #getContext() context}.
   192      * In case of <em>knockout.js</em> technology, applies given bindings 
   193      * of the current model to the <em>body</em> element of the page.
   194      */
   195     public void applyBindings() {
   196         initBindings().applyBindings(null);
   197     }
   198     
   199     /** Initializes the associated model to the specified element's subtree.
   200      * The technology is taken from the current {@link #getContext() context} and
   201      * in case of <em>knockout.js</em> applies given bindings 
   202      * of the current model to the element of the page with 'id' attribute
   203      * set to the specified <code>id</code> value.
   204      * 
   205      * @param id the id of element to apply the binding to
   206      * @since 1.1
   207      * @see Technology.ApplyId
   208      */
   209     public void applyBindings(String id) {
   210         initBindings().applyBindings(id);
   211     }
   212     
   213     /** Invokes the provided runnable in the {@link #getContext() context}
   214      * of the browser. If the caller is already on the right thread, the
   215      * <code>run.run()</code> is invoked immediately and synchronously. 
   216      * Otherwise the method returns immediately and the <code>run()</code>
   217      * method is performed later
   218      * 
   219      * @param run the action to execute
   220      */
   221     public void runInBrowser(Runnable run) {
   222         context.execute(run);
   223     }
   224 
   225     /** Invokes the specified function index in the {@link #getContext() context}
   226      * of the browser. If the caller is already on the right thread, the
   227      * index-th function is invoked immediately and synchronously. 
   228      * Otherwise the method returns immediately and the function is invoked
   229      * later.
   230      * 
   231      * @param index the index of the function as will be passed to
   232      *   {@link Type#call(java.lang.Object, int, java.lang.Object, java.lang.Object)}
   233      *   method
   234      * @param args array of arguments that will be passed as
   235      *   <code>data</code> argument of the <code>call</code> method.
   236      * @since 0.7.6
   237      */
   238     public void runInBrowser(final int index, final Object... args) {
   239         context.execute(new Runnable() {
   240             @Override
   241             public void run() {
   242                 try {
   243                     type.call(obj, index, args, null);
   244                 } catch (Exception ex) {
   245                     ex.printStackTrace();
   246                 }
   247             }
   248         });
   249     }
   250     
   251     /** Initializes the provided collection with a content of the <code>array</code>.
   252      * The initialization can only be done soon after the the collection 
   253      * is created, otherwise an exception is throw
   254      * 
   255      * @param to the collection to initialize (assumed to be empty)
   256      * @param array the array to add to the collection
   257      * @throws IllegalStateException if the system has already been initialized
   258      */
   259     public void initTo(Collection<?> to, Object array) {
   260         if (ko != null) {
   261             throw new IllegalStateException();
   262         }
   263         if (to instanceof JSONList) {
   264            ((JSONList)to).init(array);
   265         } else {
   266             JSONList.init(to, array);
   267         }
   268     }
   269 
   270     /** Takes an object representing JSON result and extract some of its
   271      * properties. It is assumed that the <code>props</code> and
   272      * <code>values</code> arrays have the same length.
   273      * 
   274      * @param json the JSON object (actual type depends on the associated
   275      *   {@link Technology})
   276      * @param props list of properties to extract
   277      * @param values array that will be filled with extracted values
   278      */
   279     public void extract(Object json, String[] props, Object[] values) {
   280         JSON.extract(context, json, props, values);
   281     }
   282 
   283     /** Converts raw JSON <code>data</code> into a Java {@link Model} class.
   284      * 
   285      * @param <T> type of the model class
   286      * @param modelClass the type of the class to create
   287      * @param data the raw JSON data
   288      * @return newly created instance of the model class
   289      */
   290     public <T> T read(Class<T> modelClass, Object data) {
   291         return JSON.read(context, modelClass, data);
   292     }
   293 
   294     /** Initializes asynchronous JSON connection to specified URL. Delegates
   295      * to {@link #loadJSON(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object, java.lang.Object...) }
   296      * with no extra parameters.
   297      * 
   298      * @param index the callback index to be used when a reply is received
   299      *   to call {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object)}.
   300      * 
   301      * @param urlBefore the part of the URL before JSON-P callback parameter
   302      * @param urlAfter the rest of the URL or <code>null</code> if no JSON-P is used
   303      * @param method method to use for connection to the server
   304      * @param data string, number or a {@link Model} generated class to send to
   305      *    the server when doing a query
   306      */
   307     public void loadJSON(final int index, 
   308         String urlBefore, String urlAfter, String method,
   309         final Object data
   310     ) {
   311         loadJSON(index, urlBefore, urlAfter, method, data, new Object[0]);
   312     }
   313     
   314     /** Initializes asynchronous JSON connection to specified URL. The 
   315      * method returns immediately and later does callback later.
   316      * 
   317      * @param index the callback index to be used when a reply is received
   318      *   to call {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object)}.
   319      * 
   320      * @param urlBefore the part of the URL before JSON-P callback parameter
   321      * @param urlAfter the rest of the URL or <code>null</code> if no JSON-P is used
   322      * @param method method to use for connection to the server
   323      * @param data string, number or a {@link Model} generated class to send to
   324      *    the server when doing a query
   325      * @param params extra params to pass back when calling
   326      *   {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object, java.lang.Object[])}
   327      * @since 0.8.1
   328      */
   329     public void loadJSON(final int index, 
   330         String urlBefore, String urlAfter, String method,
   331         final Object data, final Object... params
   332     ) {
   333         class Rcvr extends RcvrJSON {
   334             @Override
   335             protected void onMessage(MsgEvnt msg) {
   336                 type.onMessage(obj, index, 1, msg.getValues(), params);
   337             }
   338 
   339             @Override
   340             protected void onError(MsgEvnt msg) {
   341                 type.onMessage(obj, index, 2, msg.getException(), params);
   342             }
   343         }
   344         JSON.loadJSON(context, new Rcvr(), urlBefore, urlAfter, method, data);
   345     }
   346     
   347     /** Opens new WebSocket connection to the specified URL. 
   348      * 
   349      * @param index the index to use later during callbacks to 
   350      *   {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object)}
   351      * @param url the <code>ws://</code> or <code>wss://</code> URL to connect to
   352      * @param data data to send to server (usually <code>null</code>)
   353      * @return returns a non-null object representing the socket
   354      *   which can be used when calling {@link #wsSend(java.lang.Object, java.lang.String, java.lang.Object) }
   355      */
   356     public Object wsOpen(final int index, String url, Object data) {
   357         class WSrcvr extends RcvrJSON {
   358             @Override
   359             protected void onError(MsgEvnt msg) {
   360                 type.onMessage(obj, index, 2, msg.getException());
   361             }
   362             
   363             @Override
   364             protected void onMessage(MsgEvnt msg) {
   365                 type.onMessage(obj, index, 1, msg.getValues());
   366             }
   367             
   368             @Override
   369             protected void onClose(MsgEvnt msg) {
   370                 type.onMessage(obj, index, 3, null);
   371             }
   372 
   373             @Override
   374             protected void onOpen(MsgEvnt msg) {
   375                 type.onMessage(obj, index, 0, null);
   376             }
   377         }
   378         return JSON.openWS(context, new WSrcvr(), url, data);
   379     }
   380     
   381     /** Sends a message to existing socket.
   382      * 
   383      * @param webSocket the socket to send message to
   384      * @param url the <code>ws://</code> or <code>wss://</code> URL to connect to,
   385      *    preferably the same as the one used when the socket was 
   386      *    {@link #wsOpen(int, java.lang.String, java.lang.Object) opened}
   387      * @param data the data to send or <code>null</code> if the socket is
   388      *    supposed to be closed
   389      */
   390     public void wsSend(Object webSocket, String url, Object data) {
   391         ((JSON.WS)webSocket).send(context, url, data);
   392     }
   393 
   394     /** Converts raw data (one of its properties) to string representation.
   395      * 
   396      * @param data the object
   397      * @param propName the name of object property or <code>null</code>
   398      *   if the whole object should be converted
   399      * @return the string representation of the object or its property
   400      */
   401     public String toString(Object data, String propName) {
   402         return JSON.toString(context, data, propName);
   403     }
   404     
   405     /** Converts raw data (one of its properties) to a number representation.
   406      * 
   407      * @param data the object
   408      * @param propName the name of object property or <code>null</code>
   409      *   if the whole object should be converted
   410      * @return the number representation of the object or its property
   411      */
   412     public Number toNumber(Object data, String propName) {
   413         return JSON.toNumber(context, data, propName);
   414     }
   415 
   416     /** Converts raw JSON data into a {@link Model} class representation.
   417      * 
   418      * @param <T> type of the model to create
   419      * @param type class of the model to create
   420      * @param data raw JSON data (depends on associated {@link Technology})
   421      * @return new instances of the model class filled with values from the
   422      *   <code>data</code> object
   423      */
   424     public <T> T toModel(Class<T> type, Object data) {
   425         return JSON.toModel(context, type, data, null);
   426     }
   427 
   428     /** Creates new JSON like observable list.
   429      * 
   430      * @param <T> the type of the list elements
   431      * @param propName name of a property this list is associated with
   432      * @param onChange index of the property to use when the list is modified
   433      *   during callback to {@link Type#onChange(java.lang.Object, int)}
   434      * @param dependingProps the array of {@link ComputedProperty derived properties}
   435      *   that depend on the value of the list
   436      * @return new, empty list associated with this proto-object and its model
   437      */
   438     public <T> List<T> createList(String propName, int onChange, String... dependingProps) {
   439         return new JSONList<T>(this, propName, onChange, dependingProps);
   440     }
   441 
   442     /** Copies content of one collection to another, re-assigning all its
   443      * elements from their current context to the new <code>ctx</code>.
   444      * 
   445      * @param <T> type of the collections
   446      * @param to the target collection to be filled with cloned values
   447      * @param ctx context for the new collection
   448      * @param from original collection with its data
   449      */
   450     public <T> void cloneList(Collection<T> to, BrwsrCtx ctx, Collection<T> from) {
   451         Boolean isModel = null;
   452         for (T t : from) {
   453             if (isModel == null) {
   454                 isModel = JSON.isModel(t.getClass());
   455             }
   456             if (isModel) {
   457                 to.add(JSON.bindTo(t, ctx));
   458             } else {
   459                 to.add(t);
   460             }
   461         }
   462     }
   463     
   464     //
   465     // internal state
   466     //
   467     
   468     final String toStr() {
   469         return "Proto[" + obj + "]@" + Integer.toHexString(System.identityHashCode(this));
   470     }
   471     
   472     final Bindings initBindings() {
   473         if (ko == null) {
   474             Bindings b = Bindings.apply(context, obj);
   475             PropertyBinding[] pb = new PropertyBinding[type.propertyNames.length];
   476             for (int i = 0; i < pb.length; i++) {
   477                 pb[i] = b.registerProperty(
   478                     type.propertyNames[i], i, obj, type, type.propertyReadOnly[i]
   479                 );
   480             }
   481             FunctionBinding[] fb = new FunctionBinding[type.functions.length];
   482             for (int i = 0; i < fb.length; i++) {
   483                 fb[i] = FunctionBinding.registerFunction(
   484                     type.functions[i], i, obj, type
   485                 );
   486             }
   487             ko = b;
   488             b.finish(obj, pb, fb);
   489         }
   490         return ko;
   491     }
   492 
   493     final Bindings getBindings() {
   494         return ko;
   495     }
   496 
   497     final void onChange(int index) {
   498         type.onChange(obj, index);
   499     }
   500 
   501     final Observers observers(boolean create) {
   502         if (create && observers == null) {
   503             observers = new Observers();
   504         }
   505         return observers;
   506     }
   507 
   508     /** Functionality used by the code generated by annotation
   509      * processor for the {@link net.java.html.json.Model} annotation.
   510      * 
   511      * @param <Model> the generated class
   512      * @since 0.7
   513      */
   514     public static abstract class Type<Model> {
   515         private final Class<Model> clazz;
   516         private final String[] propertyNames;
   517         private final boolean[] propertyReadOnly;
   518         private final String[] functions;
   519 
   520         /** Constructor for subclasses generated by the annotation processor
   521          * associated with {@link net.java.html.json.Model} annotation.
   522          * 
   523          * @param clazz the generated model class
   524          * @param modelFor the original class annotated by the {@link net.java.html.json.Model} annotation.
   525          * @param properties number of properties the class has
   526          * @param functions  number of functions the class has
   527          */
   528         protected Type(
   529             Class<Model> clazz, Class<?> modelFor, int properties, int functions
   530         ) {
   531             assert getClass().getName().endsWith("$Html4JavaType");
   532             try {
   533                 assert getClass().getDeclaringClass() == clazz;
   534             } catch (SecurityException ex) {
   535                 // OK, no check
   536             }
   537             this.clazz = clazz;
   538             this.propertyNames = new String[properties];
   539             this.propertyReadOnly = new boolean[properties];
   540             this.functions = new String[functions];
   541             JSON.register(clazz, this);
   542         }
   543 
   544         /** Registers property for the type. It is expected each index
   545          * is initialized only once.
   546          * 
   547          * @param name name of the property
   548          * @param index index of the property
   549          * @param readOnly is the property read only?
   550          */
   551         protected final void registerProperty(String name, int index, boolean readOnly) {
   552             assert propertyNames[index] == null;
   553             propertyNames[index] = name;
   554             propertyReadOnly[index] = readOnly;
   555         }
   556 
   557         /** Registers function of given name at given index.
   558          * 
   559          * @param name name of the function
   560          * @param index name of the type
   561          */
   562         protected final void registerFunction(String name, int index) {
   563             assert functions[index] == null;
   564             functions[index] = name;
   565         }
   566         
   567         /** Creates new proto-object for given {@link Model} class bound to
   568          * provided context.
   569          * 
   570          * @param obj instance of appropriate {@link Model} class
   571          * @param context the browser context
   572          * @return new proto-object that the generated class can use for
   573          *   communication with the infrastructure
   574          */
   575         public Proto createProto(Object obj, BrwsrCtx context) {
   576             return new Proto(obj, this, context);
   577         }
   578         
   579         //
   580         // Implemented by subclasses
   581         //
   582         
   583         /** Sets value of a {@link #registerProperty(java.lang.String, int, boolean) registered property}
   584          * to new value.
   585          * 
   586          * @param model the instance of {@link Model model class}
   587          * @param index index of the property used during registration
   588          * @param value the value to set the property to
   589          */
   590         protected abstract void setValue(Model model, int index, Object value);
   591         
   592         /** Obtains and returns value of a 
   593          * {@link #registerProperty(java.lang.String, int, boolean) registered property}.
   594          * 
   595          * @param model the instance of {@link Model model class}
   596          * @param index index of the property used during registration
   597          * @return current value of the property
   598          */
   599         protected abstract Object getValue(Model model, int index);
   600         
   601         /** Invokes a {@link #registerFunction(java.lang.String, int)} registered function
   602          * on given object.
   603          * 
   604          * @param model the instance of {@link Model model class}
   605          * @param index index of the property used during registration
   606          * @param data the currently selected object the function is about to operate on
   607          * @param event the event that triggered the event
   608          * @throws Exception the method can throw exception which is then logged
   609          */
   610         protected abstract void call(Model model, int index, Object data, Object event)
   611         throws Exception;
   612         
   613         /** Re-binds the model object to new browser context.
   614          * 
   615          * @param model the instance of {@link Model model class}
   616          * @param ctx browser context to clone the object to
   617          * @return new instance of the model suitable for new context
   618          */
   619         protected abstract Model cloneTo(Model model, BrwsrCtx ctx);
   620         
   621         /** Reads raw JSON data and converts them to our model class.
   622          * 
   623          * @param c the browser context to work in
   624          * @param json raw JSON data to get values from
   625          * @return new instance of model class filled by the data
   626          */
   627         protected abstract Model read(BrwsrCtx c, Object json);
   628         
   629         /** Called when a {@link #registerProperty(java.lang.String, int, boolean) registered property}
   630          * changes its value.
   631          * 
   632          * @param model the object that has the property
   633          * @param index the index of the property during registration
   634          */
   635         protected abstract void onChange(Model model, int index);
   636         
   637         /** Finds out if there is an associated proto-object for given
   638          * object.
   639          * 
   640          * @param object an object, presumably (but not necessarily) instance of Model class
   641          * @return associated proto-object or <code>null</code>
   642          */
   643         protected abstract Proto protoFor(Object object);
   644 
   645         /** Called to report results of asynchronous over-the-wire 
   646          * communication. Result of calling {@link Proto#wsOpen(int, java.lang.String, java.lang.Object)}
   647          * or {@link Proto#loadJSON(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object, java.lang.Object...)}.
   648          * 
   649          * @param model the instance of the model class
   650          * @param index index used during initiating the communication (via <code>loadJSON</code> or <code>wsOpen</code> calls)
   651          * @param type type of the message: 0 - onOpen, 1 - onMessage, 2 - onError, 3 - onClose -
   652          *   not all messages are applicable to all communication protocols (JSON has only 1 and 2).
   653          * @param data <code>null</code> or string, number or a {@link Model} class
   654          *   obtained to the server as a response
   655          */
   656         protected void onMessage(Model model, int index, int type, Object data) {
   657             onMessage(model, index, type, data, new Object[0]);
   658         }
   659         
   660         /** Called to report results of asynchronous over-the-wire 
   661          * communication. Result of calling {@link Proto#wsOpen(int, java.lang.String, java.lang.Object)}
   662          * or {@link Proto#loadJSON(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object, java.lang.Object...)}.
   663          * 
   664          * @param model the instance of the model class
   665          * @param index index used during initiating the communication (via <code>loadJSON</code> or <code>wsOpen</code> calls)
   666          * @param type type of the message: 0 - onOpen, 1 - onMessage, 2 - onError, 3 - onClose -
   667          *   not all messages are applicable to all communication protocols (JSON has only 1 and 2).
   668          * @param data <code>null</code> or string, number or a {@link Model} class
   669          *   obtained to the server as a response
   670          * @param params extra parameters as passed for example to
   671          *   {@link Proto#loadJSON(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object, java.lang.Object...)}
   672          *   method
   673          * @since 0.8.1
   674          */
   675         protected void onMessage(Model model, int index, int type, Object data, Object[] params) {
   676             onMessage(model, index, type, data);
   677         }
   678 
   679         //
   680         // Various support methods the generated classes use
   681         //
   682 
   683         /** Converts and array of raw JSON objects into an array of typed
   684          * Java {@link Model} classes.
   685          * 
   686          * @param <T> the type of the destination array
   687          * @param context browser context to use
   688          * @param src array of raw JSON objects
   689          * @param destType type of the individual array elements
   690          * @param dest array to be filled with read type instances
   691          */
   692         public <T> void copyJSON(BrwsrCtx context, Object[] src, Class<T> destType, T[] dest) {
   693             for (int i = 0; i < src.length && i < dest.length; i++) {
   694                 dest[i] = org.netbeans.html.json.impl.JSON.read(context, destType, src[i]);
   695             }
   696         }
   697         
   698         /** Compares two objects that can be converted to integers.
   699          * @param a first value
   700          * @param b second value
   701          * @return true if they are the same
   702          */
   703         public final boolean isSame(int a, int b) {
   704             return a == b;
   705         }
   706 
   707         /** Compares two objects that can be converted to (floating point)
   708          * numbers.
   709          * @param a first value
   710          * @param b second value
   711          * @return  true if they are the same
   712          */
   713         public final boolean isSame(double a, double b) {
   714             return a == b;
   715         }
   716 
   717         /** Compares two objects for being the same - e.g. either <code>==</code>
   718          * or <code>equals</code>.
   719          * @param a first value
   720          * @param b second value
   721          * @return true if they are equals
   722          */ 
   723         public final boolean isSame(Object a, Object b) {
   724             if (a == b) {
   725                 return true;
   726             }
   727             if (a == null || b == null) {
   728                 return false;
   729             }
   730             return a.equals(b);
   731         }
   732 
   733         /** Cumulative hash function. Adds hashcode of the object to the
   734          * previous value.
   735          * @param o the object (or <code>null</code>)
   736          * @param h the previous value of the hash
   737          * @return new hash - the old one xor the object's one
   738          */
   739         public final int hashPlus(Object o, int h) {
   740             return o == null ? h : h ^ o.hashCode();
   741         }
   742         
   743         /** Converts an object to its JSON value.
   744          * 
   745          * @param obj the object to convert
   746          * @return JSON representation of the object
   747          */
   748         public final String toJSON(Object obj) {
   749             return JSON.toJSON(obj);
   750         }
   751         
   752         /** Converts the value to string.
   753          * 
   754          * @param val the value
   755          * @return the converted value
   756          */
   757         public final String stringValue(Object val) {
   758             return JSON.stringValue(val);
   759         }
   760 
   761         /** Converts the value to number.
   762          * 
   763          * @param val the value
   764          * @return the converted value
   765          */
   766         public final Number numberValue(Object val) {
   767             return JSON.numberValue(val);
   768         }
   769 
   770         /** Converts the value to character.
   771          * 
   772          * @param val the value
   773          * @return the converted value
   774          */
   775         public final Character charValue(Object val) {
   776             return JSON.charValue(val);
   777         }
   778 
   779         /** Converts the value to boolean.
   780          * 
   781          * @param val the value
   782          * @return the converted value
   783          */
   784         public final Boolean boolValue(Object val) {
   785             return JSON.boolValue(val);
   786         }
   787         
   788         /** Extracts value of specific type from given object.
   789          * 
   790          * @param <T> the type of object one is interested in
   791          * @param type the type
   792          * @param val the object to convert to type
   793          * @return the converted value
   794          */
   795         public final <T> T extractValue(Class<T> type, Object val) {
   796             if (Number.class.isAssignableFrom(type)) {
   797                 val = numberValue(val);
   798             }
   799             if (Boolean.class == type) {
   800                 val = boolValue(val);
   801             }
   802             if (String.class == type) {
   803                 val = stringValue(val);
   804             }
   805             if (Character.class == type) {
   806                 val = charValue(val);
   807             }
   808             if (Integer.class == type) {
   809                 val = val instanceof Number ? ((Number) val).intValue() : 0;
   810             }
   811             if (Long.class == type) {
   812                 val = val instanceof Number ? ((Number) val).longValue() : 0;
   813             }
   814             if (Short.class == type) {
   815                 val = val instanceof Number ? ((Number) val).shortValue() : 0;
   816             }
   817             if (Byte.class == type) {
   818                 val = val instanceof Number ? ((Number) val).byteValue() : 0;
   819             }
   820             if (Double.class == type) {
   821                 val = val instanceof Number ? ((Number) val).doubleValue() : Double.NaN;
   822             }
   823             if (Float.class == type) {
   824                 val = val instanceof Number ? ((Number) val).floatValue() : Float.NaN;
   825             }
   826             if (type.isEnum() && val instanceof String) {
   827                 val = Enum.valueOf(type.asSubclass(Enum.class), (String)val);
   828             }
   829             return type.cast(val);
   830         }
   831 
   832         /** Special dealing with array &amp; {@link List} values. This method
   833          * takes the provided collection, empties it and fills it again
   834          * with values extracted from <code>value</code> (which is supposed
   835          * to be an array).
   836          * 
   837          * @param <T> the type of list elements
   838          * @param arr collection to fill with elements in value
   839          * @param type the type of elements in the collection
   840          * @param value array of elements to put into the collecition. If
   841          *   value is not an array it is wrapped into array with only element
   842          * @since 1.0
   843          */
   844         public final <T> void replaceValue(Collection<? super T> arr, Class<T> type, Object value) {
   845             Object[] newArr;
   846             if (value instanceof Object[]) {
   847                 newArr = (Object[]) value;
   848             } else {
   849                 newArr = new Object[] { value };
   850             }
   851             arr.clear();
   852             for (Object e : newArr) {
   853                 arr.add(extractValue(type, e));
   854             }
   855         }
   856     }
   857 }