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