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