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