json/src/main/java/org/apidesign/html/json/spi/Proto.java
author Jaroslav Tulach <jaroslav.tulach@netbeans.org>
Fri, 03 Jan 2014 08:16:39 +0100
branchosgi
changeset 386 2fc4100fcd32
parent 385 68012e8398a8
child 387 972a38bebd50
permissions -rw-r--r--
Making JSON calls work without references to non-public packages
jaroslav@376
     1
/**
jaroslav@373
     2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
jaroslav@373
     3
 *
jaroslav@376
     4
 * Copyright 2013-2013 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@376
    30
 * Software is Oracle. Portions Copyright 2013-2013 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@374
    48
import org.netbeans.html.json.impl.Bindings;
jaroslav@374
    49
import org.netbeans.html.json.impl.JSON;
jaroslav@383
    50
import org.netbeans.html.json.impl.JSONList;
jaroslav@386
    51
import org.netbeans.html.json.impl.RcvrJSON;
jaroslav@386
    52
import org.netbeans.html.json.impl.RcvrJSON.MsgEvnt;
jaroslav@373
    53
jaroslav@373
    54
/**
jaroslav@373
    55
 *
jaroslav@373
    56
 * @author Jaroslav Tulach <jtulach@netbeans.org>
jaroslav@373
    57
 * @since 0.7
jaroslav@373
    58
 */
jaroslav@373
    59
public final class Proto {
jaroslav@374
    60
    private final Object obj;
jaroslav@373
    61
    private final Type type;
jaroslav@373
    62
    private final net.java.html.BrwsrCtx context;
jaroslav@373
    63
    private boolean locked;
jaroslav@374
    64
    private org.netbeans.html.json.impl.Bindings ko;
jaroslav@373
    65
jaroslav@374
    66
    Proto(Object obj, Type type, BrwsrCtx context) {
jaroslav@374
    67
        this.obj = obj;
jaroslav@373
    68
        this.type = type;
jaroslav@373
    69
        this.context = context;
jaroslav@373
    70
    }
jaroslav@373
    71
jaroslav@373
    72
    public BrwsrCtx getContext() {
jaroslav@373
    73
        return context;
jaroslav@373
    74
    }
jaroslav@373
    75
    
jaroslav@373
    76
    public void acquireLock() throws IllegalStateException {
jaroslav@373
    77
        if (locked) throw new IllegalStateException();
jaroslav@373
    78
        locked = true;
jaroslav@373
    79
    }
jaroslav@373
    80
    
jaroslav@373
    81
    public void checkLock() throws IllegalStateException {
jaroslav@373
    82
        if (locked) throw new IllegalStateException();
jaroslav@373
    83
    }
jaroslav@373
    84
    
jaroslav@373
    85
    public void releaseLock() {
jaroslav@373
    86
        locked = false;
jaroslav@373
    87
    }
jaroslav@373
    88
    
jaroslav@373
    89
    public void valueHasMutated(String propName) {
jaroslav@374
    90
        if (ko != null) {
jaroslav@374
    91
            ko.valueHasMutated(propName);
jaroslav@374
    92
        }
jaroslav@373
    93
    }
jaroslav@373
    94
    
jaroslav@373
    95
    public void applyBindings() {
jaroslav@374
    96
        initBindings().applyBindings();
jaroslav@374
    97
    }
jaroslav@374
    98
    
jaroslav@381
    99
    public void runInBrowser(Runnable run) {
jaroslav@381
   100
        JSON.runInBrowser(context, run);
jaroslav@381
   101
    }
jaroslav@383
   102
jaroslav@383
   103
    public void initTo(Collection<?> to, Object array) {
jaroslav@383
   104
        if (ko != null) {
jaroslav@383
   105
            throw new IllegalStateException();
jaroslav@383
   106
        }
jaroslav@383
   107
        if (to instanceof JSONList) {
jaroslav@383
   108
           ((JSONList)to).init(array);
jaroslav@383
   109
        } else {
jaroslav@383
   110
            JSONList.init(to, array);
jaroslav@383
   111
        }
jaroslav@383
   112
    }
jaroslav@383
   113
    
jaroslav@385
   114
    public void extract(Object json, String[] props, Object[] values) {
jaroslav@385
   115
        JSON.extract(context, json, props, values);
jaroslav@385
   116
    }
jaroslav@385
   117
    
jaroslav@385
   118
    public <T> T read(Class<T> modelClass, Object data) {
jaroslav@385
   119
        return JSON.read(context, modelClass, data);
jaroslav@385
   120
    }
jaroslav@381
   121
    
jaroslav@376
   122
    // XXX: Don't expose internal type
jaroslav@376
   123
    public Bindings initBindings() {
jaroslav@374
   124
        if (ko == null) {
jaroslav@374
   125
            Bindings b = Bindings.apply(context, obj);
jaroslav@374
   126
            PropertyBinding[] pb = new PropertyBinding[type.propertyNames.length];
jaroslav@374
   127
            for (int i = 0; i < pb.length; i++) {
jaroslav@374
   128
                pb[i] = b.registerProperty(
jaroslav@374
   129
                    type.propertyNames[i], i, obj, type, type.propertyReadOnly[i]
jaroslav@374
   130
                );
jaroslav@374
   131
            }
jaroslav@374
   132
            FunctionBinding[] fb = new FunctionBinding[type.functions.length];
jaroslav@374
   133
            for (int i = 0; i < fb.length; i++) {
jaroslav@375
   134
                fb[i] = FunctionBinding.registerFunction(
jaroslav@374
   135
                    type.functions[i], i, obj, type
jaroslav@374
   136
                );
jaroslav@374
   137
            }
jaroslav@378
   138
            ko = b;
jaroslav@374
   139
            b.finish(obj, pb, fb);
jaroslav@374
   140
        }
jaroslav@374
   141
        return ko;
jaroslav@374
   142
    }
jaroslav@374
   143
jaroslav@376
   144
    // XXX: Don't expose internal type
jaroslav@374
   145
    public Bindings getBindings() {
jaroslav@374
   146
        return ko;
jaroslav@373
   147
    }
jaroslav@383
   148
    
jaroslav@383
   149
    // XXX: Can be hidden too
jaroslav@383
   150
    public void onChange(int index) {
jaroslav@383
   151
        type.onChange(obj, index);
jaroslav@383
   152
    }
jaroslav@380
   153
jaroslav@386
   154
    public void loadJSON(final int index, 
jaroslav@386
   155
        String urlBefore, String urlAfter, String method,
jaroslav@386
   156
        final Object data
jaroslav@386
   157
    ) {
jaroslav@386
   158
        class Rcvr extends RcvrJSON {
jaroslav@386
   159
            @Override
jaroslav@386
   160
            protected void onMessage(MsgEvnt msg) {
jaroslav@386
   161
                type.onMessage(obj, index, 1, msg.getValues());
jaroslav@386
   162
            }
jaroslav@386
   163
jaroslav@386
   164
            @Override
jaroslav@386
   165
            protected void onError(MsgEvnt msg) {
jaroslav@386
   166
                type.onMessage(obj, index, 2, msg.getException());
jaroslav@386
   167
            }
jaroslav@386
   168
        }
jaroslav@386
   169
        JSON.loadJSON(context, new Rcvr(), urlBefore, urlAfter, method, data);
jaroslav@386
   170
    }
jaroslav@386
   171
    
jaroslav@380
   172
    public String toString(Object data, String propName) {
jaroslav@380
   173
        return JSON.toString(context, data, propName);
jaroslav@380
   174
    }
jaroslav@380
   175
    public Number toNumber(Object data, String propName) {
jaroslav@380
   176
        return JSON.toNumber(context, data, propName);
jaroslav@380
   177
    }
jaroslav@373
   178
    
jaroslav@380
   179
    public <T> T toModel(Class<T> type, Object data, String propName) {
jaroslav@380
   180
        return JSON.toModel(context, type, data, propName);
jaroslav@380
   181
    }
jaroslav@383
   182
jaroslav@383
   183
    public <T> List<T> createList(String propName, int onChange, String... dependingProps) {
jaroslav@383
   184
        return new JSONList<T>(this, propName, onChange, dependingProps);
jaroslav@383
   185
    }
jaroslav@383
   186
    
jaroslav@383
   187
    public <T> void cloneList(Collection<T> to, BrwsrCtx ctx, Collection<T> from) {
jaroslav@383
   188
        Boolean isModel = null;
jaroslav@383
   189
        for (T t : from) {
jaroslav@383
   190
            if (isModel == null) {
jaroslav@383
   191
                isModel = JSON.isModel(t.getClass());
jaroslav@383
   192
            }
jaroslav@383
   193
            if (isModel) {
jaroslav@383
   194
                to.add(JSON.bindTo(t, ctx));
jaroslav@383
   195
            } else {
jaroslav@383
   196
                to.add(t);
jaroslav@383
   197
            }
jaroslav@383
   198
        }
jaroslav@383
   199
    }
jaroslav@386
   200
jaroslav@373
   201
    /** Functionality used by the code generated by annotation
jaroslav@373
   202
     * processor for the {@link net.java.html.json.Model} annotation.
jaroslav@373
   203
     * 
jaroslav@373
   204
     * @param <Model> the generated class
jaroslav@373
   205
     * @since 0.7
jaroslav@373
   206
     */
jaroslav@373
   207
    public static abstract class Type<Model> {
jaroslav@373
   208
        private final Class<Model> clazz;
jaroslav@373
   209
        private final String[] propertyNames;
jaroslav@373
   210
        private final boolean[] propertyReadOnly;
jaroslav@373
   211
        private final String[] functions;
jaroslav@373
   212
jaroslav@373
   213
        protected Type(
jaroslav@373
   214
            Class<Model> clazz, Class<?> modelFor, int properties, int functions
jaroslav@373
   215
        ) {
jaroslav@374
   216
            assert getClass().getName().endsWith("$Html4JavaType");
jaroslav@374
   217
            assert getClass().getDeclaringClass() == clazz;
jaroslav@373
   218
            this.clazz = clazz;
jaroslav@373
   219
            this.propertyNames = new String[properties];
jaroslav@373
   220
            this.propertyReadOnly = new boolean[properties];
jaroslav@373
   221
            this.functions = new String[functions];
jaroslav@374
   222
            JSON.register(clazz, this);
jaroslav@373
   223
        }
jaroslav@373
   224
        
jaroslav@373
   225
        protected final void registerProperty(String name, int index, boolean readOnly) {
jaroslav@373
   226
            assert propertyNames[index] == null;
jaroslav@373
   227
            propertyNames[index] = name;
jaroslav@373
   228
            propertyReadOnly[index] = readOnly;
jaroslav@373
   229
        }
jaroslav@373
   230
        
jaroslav@373
   231
        protected final void registerFunction(String name, int index) {
jaroslav@373
   232
            assert functions[index] == null;
jaroslav@373
   233
            functions[index] = name;
jaroslav@373
   234
        }
jaroslav@373
   235
        
jaroslav@374
   236
        public Proto protoFor(Object obj, BrwsrCtx context) {
jaroslav@374
   237
            return new Proto(obj, this, context);
jaroslav@373
   238
        }
jaroslav@373
   239
        
jaroslav@374
   240
        // XXX: should be protected
jaroslav@374
   241
        public abstract void setValue(Model model, int index, Object value);
jaroslav@374
   242
        public abstract Object getValue(Model model, int index);
jaroslav@374
   243
        public abstract void call(Model model, int index, Object data, Object event);
jaroslav@374
   244
        public abstract Model cloneTo(Object model, BrwsrCtx ctx);
jaroslav@374
   245
        public abstract Model read(BrwsrCtx c, Object json);
jaroslav@383
   246
        public abstract void onChange(Model model, int index);
jaroslav@384
   247
        public abstract Proto protoFor(Object object);
jaroslav@386
   248
jaroslav@386
   249
        /**
jaroslav@386
   250
         * 
jaroslav@386
   251
         * @param model
jaroslav@386
   252
         * @param index
jaroslav@386
   253
         * @param type 0 - onOpen, 1 - onMessage, 2 - onError, 3 - onClose
jaroslav@386
   254
         * @param data 
jaroslav@386
   255
         */
jaroslav@386
   256
        public abstract void onMessage(Model model, int index, int type, Object data);
jaroslav@386
   257
jaroslav@386
   258
        public <T> void copyJSON(BrwsrCtx context, Object[] src, Class<T> destType, T[] dest) {
jaroslav@386
   259
            for (int i = 0; i < src.length && i < dest.length; i++) {
jaroslav@386
   260
                dest[i] = org.netbeans.html.json.impl.JSON.read(context, destType, src[i]);
jaroslav@386
   261
            }
jaroslav@386
   262
        }
jaroslav@380
   263
        
jaroslav@380
   264
        //
jaroslav@380
   265
        // Various support methods the generated classes use
jaroslav@380
   266
        //
jaroslav@380
   267
        
jaroslav@380
   268
        /** Compares two objects that can be converted to integers.
jaroslav@380
   269
         * @return true if they are the same
jaroslav@380
   270
         */
jaroslav@380
   271
        public final boolean isSame(int a, int b) {
jaroslav@380
   272
            return a == b;
jaroslav@380
   273
        }
jaroslav@380
   274
jaroslav@380
   275
        /** Compares two objects that can be converted to (floating point)
jaroslav@380
   276
         * numbers.
jaroslav@380
   277
         * @return  true if they are the same
jaroslav@380
   278
         */
jaroslav@380
   279
        public final boolean isSame(double a, double b) {
jaroslav@380
   280
            return a == b;
jaroslav@380
   281
        }
jaroslav@380
   282
jaroslav@380
   283
        /** Compares two objects for being the same - e.g. either <code>==</code>
jaroslav@380
   284
         * or <code>equals</code>.
jaroslav@380
   285
         * @return true if they are equals
jaroslav@380
   286
         */ 
jaroslav@380
   287
        public final boolean isSame(Object a, Object b) {
jaroslav@380
   288
            if (a == b) {
jaroslav@380
   289
                return true;
jaroslav@380
   290
            }
jaroslav@380
   291
            if (a == null || b == null) {
jaroslav@380
   292
                return false;
jaroslav@380
   293
            }
jaroslav@380
   294
            return a.equals(b);
jaroslav@380
   295
        }
jaroslav@380
   296
jaroslav@380
   297
        /** Cumulative hash function. Adds hashcode of the object to the
jaroslav@380
   298
         * previous value.
jaroslav@380
   299
         * @param o the object (or <code>null</code>)
jaroslav@380
   300
         * @param h the previous value of the hash
jaroslav@380
   301
         * @return new hash - the old one xor the object's one
jaroslav@380
   302
         */
jaroslav@380
   303
        public final int hashPlus(Object o, int h) {
jaroslav@380
   304
            return o == null ? h : h ^ o.hashCode();
jaroslav@380
   305
        }
jaroslav@380
   306
        
jaroslav@380
   307
        /** Converts an object to its JSON value.
jaroslav@380
   308
         * 
jaroslav@380
   309
         * @param obj the object to convert
jaroslav@380
   310
         * @return JSON representation of the object
jaroslav@380
   311
         */
jaroslav@380
   312
        public final String toJSON(Object obj) {
jaroslav@380
   313
            return JSON.toJSON(obj);
jaroslav@380
   314
        }
jaroslav@380
   315
        
jaroslav@380
   316
        /** Converts the value to string.
jaroslav@380
   317
         * 
jaroslav@380
   318
         * @param val the value
jaroslav@380
   319
         * @return the converted value
jaroslav@380
   320
         */
jaroslav@380
   321
        public final String stringValue(Object val) {
jaroslav@380
   322
            return JSON.stringValue(val);
jaroslav@380
   323
        }
jaroslav@380
   324
jaroslav@380
   325
        /** Converts the value to number.
jaroslav@380
   326
         * 
jaroslav@380
   327
         * @param val the value
jaroslav@380
   328
         * @return the converted value
jaroslav@380
   329
         */
jaroslav@380
   330
        public final Number numberValue(Object val) {
jaroslav@380
   331
            return JSON.numberValue(val);
jaroslav@380
   332
        }
jaroslav@380
   333
jaroslav@380
   334
        /** Converts the value to character.
jaroslav@380
   335
         * 
jaroslav@380
   336
         * @param val the value
jaroslav@380
   337
         * @return the converted value
jaroslav@380
   338
         */
jaroslav@380
   339
        public final Character charValue(Object val) {
jaroslav@380
   340
            return JSON.charValue(val);
jaroslav@380
   341
        }
jaroslav@380
   342
jaroslav@380
   343
        /** Converts the value to boolean.
jaroslav@380
   344
         * 
jaroslav@380
   345
         * @param val the value
jaroslav@380
   346
         * @return the converted value
jaroslav@380
   347
         */
jaroslav@380
   348
        public final Boolean boolValue(Object val) {
jaroslav@380
   349
            return JSON.boolValue(val);
jaroslav@380
   350
        }
jaroslav@380
   351
        
jaroslav@380
   352
        /** Extracts value of specific type from given object.
jaroslav@380
   353
         * 
jaroslav@380
   354
         * @param <T> the type of object one is interested in
jaroslav@380
   355
         * @param type the type
jaroslav@380
   356
         * @param val the object to convert to type
jaroslav@380
   357
         * @return the converted value
jaroslav@380
   358
         */
jaroslav@380
   359
        public final <T> T extractValue(Class<T> type, Object val) {
jaroslav@380
   360
            if (Number.class.isAssignableFrom(type)) {
jaroslav@380
   361
                val = numberValue(val);
jaroslav@380
   362
            }
jaroslav@380
   363
            if (Boolean.class == type) {
jaroslav@380
   364
                val = boolValue(val);
jaroslav@380
   365
            }
jaroslav@380
   366
            if (String.class == type) {
jaroslav@380
   367
                val = stringValue(val);
jaroslav@380
   368
            }
jaroslav@380
   369
            if (Character.class == type) {
jaroslav@380
   370
                val = charValue(val);
jaroslav@380
   371
            }
jaroslav@380
   372
            if (Integer.class == type) {
jaroslav@380
   373
                val = val instanceof Number ? ((Number) val).intValue() : 0;
jaroslav@380
   374
            }
jaroslav@380
   375
            if (Long.class == type) {
jaroslav@380
   376
                val = val instanceof Number ? ((Number) val).longValue() : 0;
jaroslav@380
   377
            }
jaroslav@380
   378
            if (Short.class == type) {
jaroslav@380
   379
                val = val instanceof Number ? ((Number) val).shortValue() : 0;
jaroslav@380
   380
            }
jaroslav@380
   381
            if (Byte.class == type) {
jaroslav@380
   382
                val = val instanceof Number ? ((Number) val).byteValue() : 0;
jaroslav@380
   383
            }
jaroslav@380
   384
            if (Double.class == type) {
jaroslav@380
   385
                val = val instanceof Number ? ((Number) val).doubleValue() : Double.NaN;
jaroslav@380
   386
            }
jaroslav@380
   387
            if (Float.class == type) {
jaroslav@380
   388
                val = val instanceof Number ? ((Number) val).floatValue() : Float.NaN;
jaroslav@380
   389
            }
jaroslav@380
   390
            return type.cast(val);
jaroslav@380
   391
        }
jaroslav@384
   392
jaroslav@373
   393
    }
jaroslav@373
   394
}