#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
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4 * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
6 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7 * Other names may be trademarks of their respective owners.
9 * The contents of this file are subject to the terms of either the GNU
10 * General Public License Version 2 only ("GPL") or the Common
11 * Development and Distribution License("CDDL") (collectively, the
12 * "License"). You may not use this file except in compliance with the
13 * License. You can obtain a copy of the License at
14 * http://www.netbeans.org/cddl-gplv2.html
15 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16 * specific language governing permissions and limitations under the
17 * License. When distributing the software, include this License Header
18 * Notice in each file and include the License file at
19 * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
20 * particular file as subject to the "Classpath" exception as provided
21 * by Oracle in the GPL Version 2 section of the License file that
22 * accompanied this code. If applicable, add the following below the
23 * License Header, with the fields enclosed by brackets [] replaced by
24 * your own identifying information:
25 * "Portions Copyrighted [year] [name of copyright owner]"
29 * The Original Software is NetBeans. The Initial Developer of the Original
30 * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
32 * If you wish your version of this file to be governed by only the CDDL
33 * or only the GPL Version 2, indicate your decision by adding
34 * "[Contributor] elects to include this software in this distribution
35 * under the [CDDL or GPL Version 2] license." If you do not indicate a
36 * single choice of license, a recipient has the option to distribute
37 * your version of this file under either the CDDL, the GPL Version 2 or
38 * to extend the choice of license to its licensees as provided above.
39 * However, if you add GPL Version 2 code and therefore, elected the GPL
40 * Version 2 license, then the option applies only if the new code is
41 * made subject to such option by the copyright holder.
43 package org.netbeans.html.json.spi;
45 import java.util.ArrayList;
46 import java.util.Collection;
47 import java.util.List;
48 import net.java.html.BrwsrCtx;
49 import net.java.html.json.ComputedProperty;
50 import net.java.html.json.Model;
51 import net.java.html.json.Property;
52 import org.netbeans.html.json.impl.Bindings;
53 import org.netbeans.html.json.impl.JSON;
54 import org.netbeans.html.json.impl.JSON.WS;
55 import org.netbeans.html.json.impl.JSONList;
56 import org.netbeans.html.json.impl.PropertyBindingAccessor;
57 import org.netbeans.html.json.impl.RcvrJSON;
58 import org.netbeans.html.json.impl.RcvrJSON.MsgEvnt;
60 /** Object associated with one instance of a model generated by the
61 * {@link Model} annotation. Contains methods the generated class can
62 * use to communicate with behind the scene associated {@link Technology}.
63 * Each {@link Proto} object is associated with <a href="http://wiki.apidesign.org/wiki/Singletonizer">
64 * singletonizer</a>-like interface {@link Type} which provides the
65 * associated {@link Technology} the necessary information about the
66 * generated {@link Model} class.
68 * @author Jaroslav Tulach
71 public final class Proto {
72 private final Object obj;
73 private final Type type;
74 private final net.java.html.BrwsrCtx context;
75 private org.netbeans.html.json.impl.Bindings ko;
76 private Observers observers;
78 Proto(Object obj, Type type, BrwsrCtx context) {
81 this.context = context;
84 /** Browser context this proto object and its associated model
87 * @return the associated context
89 public BrwsrCtx getContext() {
93 /** Acquires global lock to compute a {@link ComputedProperty derived property}
94 * on this proto object. This proto object must not be locked yet. No
95 * dependency tracking is performed.
97 * @throws IllegalStateException if already locked
99 public void acquireLock() throws IllegalStateException {
103 /** Acquires global lock to compute a {@link ComputedProperty derived property}
104 * on this proto object. This proto object must not be locked yet. The
105 * name of the property is used to track dependencies on own
106 * properties of other proto objects - when they are changed, this
107 * {@link #valueHasMutated(java.lang.String) property is changed too}.
109 * @param propName name of property we are about to compute
110 * @throws IllegalStateException thrown when there is a cyclic
114 public void acquireLock(String propName) throws IllegalStateException {
115 Observers.beginComputing(this, propName);
118 /** A property on this proto object is about to be accessed. Verifies
119 * whether this proto object is accessible - e.g. it has not been
120 * {@link #acquireLock() locked yet}. If everything is OK, the
121 * <code>propName</code> is recorded in the chain of dependencies
122 * tracked by {@link #acquireLock(java.lang.String)} and watched by
123 * {@link #valueHasMutated(java.lang.String)}.
125 * @param propName name of the property that is requested
126 * @throws IllegalStateException if the model is locked
129 public void accessProperty(String propName) throws IllegalStateException {
130 Observers.accessingValue(this, propName);
133 /** Verifies the model is not locked otherwise throws an exception.
134 * @throws IllegalStateException if the model is locked
136 public void verifyUnlocked() throws IllegalStateException {
137 Observers.verifyUnlocked(this);
140 /** When modifications are over, the model is switched into
141 * unlocked state by calling this method.
143 public void releaseLock() {
144 Observers.finishComputing(this);
147 /** Whenever model changes a property. It should notify the
148 * associated technology by calling this method.
149 * Since 0.8.3: This method may be called by any thread - it reschedules
150 * its actual execution into appropriate one by using
151 * {@link BrwsrCtx#execute(java.lang.Runnable)}.
153 * @param propName name of the changed property
155 public void valueHasMutated(final String propName) {
156 context.execute(new Runnable() {
160 ko.valueHasMutated(propName, null, null);
162 Observers.valueHasMutated(Proto.this, propName);
167 /** Whenever model changes a propertyit should notify the
168 * associated technology. Either by calling this method
169 * (if the new value is known and different to the old one) or
170 * via (slightly ineffective) {@link #valueHasMutated(java.lang.String)}
172 * Since 0.8.3: This method may be called by any thread - it reschedules
173 * its actual execution into appropriate one by using
174 * {@link BrwsrCtx#execute(java.lang.Runnable)}.
176 * @param propName name of the changed property
177 * @param oldValue provides previous value of the property
178 * @param newValue provides new value of the property
181 public void valueHasMutated(
182 final String propName, final Object oldValue, final Object newValue
184 context.execute(new Runnable() {
188 ko.valueHasMutated(propName, oldValue, newValue);
190 Observers.valueHasMutated(Proto.this, propName);
195 /** Initializes the associated model in the current {@link #getContext() context}.
196 * In case of <em>knockout.js</em> technology, applies given bindings
197 * of the current model to the <em>body</em> element of the page.
199 public void applyBindings() {
200 initBindings(null).applyBindings(null);
203 /** Initializes the associated model to the specified element's subtree.
204 * The technology is taken from the current {@link #getContext() context} and
205 * in case of <em>knockout.js</em> applies given bindings
206 * of the current model to the element of the page with 'id' attribute
207 * set to the specified <code>id</code> value.
209 * @param id the id of element to apply the binding to
211 * @see Technology.ApplyId
213 public void applyBindings(String id) {
214 initBindings(null).applyBindings(id);
217 /** Invokes the provided runnable in the {@link #getContext() context}
218 * of the browser. If the caller is already on the right thread, the
219 * <code>run.run()</code> is invoked immediately and synchronously.
220 * Otherwise the method returns immediately and the <code>run()</code>
221 * method is performed later
223 * @param run the action to execute
225 public void runInBrowser(Runnable run) {
226 context.execute(run);
229 /** Invokes the specified function index in the {@link #getContext() context}
230 * of the browser. If the caller is already on the right thread, the
231 * index-th function is invoked immediately and synchronously.
232 * Otherwise the method returns immediately and the function is invoked
235 * @param index the index of the function as will be passed to
236 * {@link Type#call(java.lang.Object, int, java.lang.Object, java.lang.Object)}
238 * @param args array of arguments that will be passed as
239 * <code>data</code> argument of the <code>call</code> method.
242 public void runInBrowser(final int index, final Object... args) {
243 context.execute(new Runnable() {
247 type.call(obj, index, args, null);
248 } catch (Exception ex) {
249 ex.printStackTrace();
255 /** Initializes the provided collection with a content of the <code>array</code>.
256 * The initialization can only be done soon after the the collection
257 * is created, otherwise an exception is throw
259 * @param to the collection to initialize (assumed to be empty)
260 * @param array the array to add to the collection
261 * @throws IllegalStateException if the system has already been initialized
263 public void initTo(Collection<?> to, Object array) {
265 throw new IllegalStateException();
267 if (to instanceof JSONList) {
268 ((JSONList)to).init(array);
270 JSONList.init(to, array);
274 /** Takes an object representing JSON result and extract some of its
275 * properties. It is assumed that the <code>props</code> and
276 * <code>values</code> arrays have the same length.
278 * @param json the JSON object (actual type depends on the associated
279 * {@link Technology})
280 * @param props list of properties to extract
281 * @param values array that will be filled with extracted values
283 public void extract(Object json, String[] props, Object[] values) {
284 JSON.extract(context, json, props, values);
287 /** Converts raw JSON <code>data</code> into a Java {@link Model} class.
289 * @param <T> type of the model class
290 * @param modelClass the type of the class to create
291 * @param data the raw JSON data
292 * @return newly created instance of the model class
294 public <T> T read(Class<T> modelClass, Object data) {
295 return JSON.read(context, modelClass, data);
298 /** Initializes asynchronous JSON connection to specified URL. Delegates
299 * to {@link #loadJSON(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object, java.lang.Object...) }
300 * with no extra parameters.
302 * @param index the callback index to be used when a reply is received
303 * to call {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object)}.
305 * @param urlBefore the part of the URL before JSON-P callback parameter
306 * @param urlAfter the rest of the URL or <code>null</code> if no JSON-P is used
307 * @param method method to use for connection to the server
308 * @param data string, number or a {@link Model} generated class to send to
309 * the server when doing a query
311 public void loadJSON(final int index,
312 String urlBefore, String urlAfter, String method,
315 loadJSON(index, urlBefore, urlAfter, method, data, new Object[0]);
318 /** Initializes asynchronous JSON connection to specified URL. The
319 * method returns immediately and later does callback later.
321 * @param index the callback index to be used when a reply is received
322 * to call {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object)}.
324 * @param urlBefore the part of the URL before JSON-P callback parameter
325 * @param urlAfter the rest of the URL or <code>null</code> if no JSON-P is used
326 * @param method method to use for connection to the server
327 * @param data string, number or a {@link Model} generated class to send to
328 * the server when doing a query
329 * @param params extra params to pass back when calling
330 * {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object, java.lang.Object[])}
333 public void loadJSON(final int index,
334 String urlBefore, String urlAfter, String method,
335 final Object data, final Object... params
337 loadJSONWithHeaders(index, null, urlBefore, urlAfter, method, data, params);
340 /** Initializes asynchronous JSON connection to specified URL. The
341 * method returns immediately and later does callback later.
343 * @param index the callback index to be used when a reply is received
344 * to call {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object)}.
346 * @param headers headers to use for the request or <code>null</code> to use default ones
347 * @param urlBefore the part of the URL before JSON-P callback parameter
348 * @param urlAfter the rest of the URL or <code>null</code> if no JSON-P is used
349 * @param method method to use for connection to the server
350 * @param data string, number or a {@link Model} generated class to send to
351 * the server when doing a query
352 * @param params extra params to pass back when calling
353 * {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object, java.lang.Object[])}
356 public void loadJSONWithHeaders(final int index,
358 String urlBefore, String urlAfter, String method,
359 final Object data, final Object... params
361 class Rcvr extends RcvrJSON {
363 protected void onMessage(MsgEvnt msg) {
364 type.onMessage(obj, index, 1, msg.getValues(), params);
368 protected void onError(MsgEvnt msg) {
369 type.onMessage(obj, index, 2, msg.getException(), params);
372 JSONCall call = PropertyBindingAccessor.createCall(
373 context, new Rcvr(), headers, urlBefore, urlAfter, method, data
375 Transfer t = JSON.findTransfer(context);
379 /** Opens new WebSocket connection to the specified URL.
381 * @param index the index to use later during callbacks to
382 * {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object)}
383 * @param url the <code>ws://</code> or <code>wss://</code> URL to connect to
384 * @param data data to send to server (usually <code>null</code>)
385 * @return returns a non-null object representing the socket
386 * which can be used when calling {@link #wsSend(java.lang.Object, java.lang.String, java.lang.Object) }
388 public Object wsOpen(final int index, String url, Object data) {
389 class WSrcvr extends RcvrJSON {
391 protected void onError(MsgEvnt msg) {
392 type.onMessage(obj, index, 2, msg.getException());
396 protected void onMessage(MsgEvnt msg) {
397 type.onMessage(obj, index, 1, msg.getValues());
401 protected void onClose(MsgEvnt msg) {
402 type.onMessage(obj, index, 3, null);
406 protected void onOpen(MsgEvnt msg) {
407 type.onMessage(obj, index, 0, null);
410 WS ws = WS.create(JSON.findWSTransfer(context), new WSrcvr());
411 ws.send(context, null, url, data);
415 /** Sends a message to existing socket.
417 * @param webSocket the socket to send message to
418 * @param url the <code>ws://</code> or <code>wss://</code> URL to connect to,
419 * preferably the same as the one used when the socket was
420 * {@link #wsOpen(int, java.lang.String, java.lang.Object) opened}
421 * @param data the data to send or <code>null</code> if the socket is
422 * supposed to be closed
424 public void wsSend(Object webSocket, String url, Object data) {
425 ((JSON.WS)webSocket).send(context, null, url, data);
428 /** Converts raw data (one of its properties) to string representation.
430 * @param data the object
431 * @param propName the name of object property or <code>null</code>
432 * if the whole object should be converted
433 * @return the string representation of the object or its property
435 public String toString(Object data, String propName) {
436 return JSON.toString(context, data, propName);
439 /** Converts raw data (one of its properties) to a number representation.
441 * @param data the object
442 * @param propName the name of object property or <code>null</code>
443 * if the whole object should be converted
444 * @return the number representation of the object or its property
446 public Number toNumber(Object data, String propName) {
447 return JSON.toNumber(context, data, propName);
450 /** Converts raw JSON data into a {@link Model} class representation.
452 * @param <T> type of the model to create
453 * @param type class of the model to create
454 * @param data raw JSON data (depends on associated {@link Technology})
455 * @return new instances of the model class filled with values from the
456 * <code>data</code> object
458 public <T> T toModel(Class<T> type, Object data) {
459 return JSON.toModel(context, type, data, null);
462 /** Creates new JSON like observable list.
464 * @param <T> the type of the list elements
465 * @param propName name of a property this list is associated with
466 * @param onChange index of the property to use when the list is modified
467 * during callback to {@link Type#onChange(java.lang.Object, int)}.
468 * If the value is {@link Integer#MIN_VALUE}, then the list is
469 * not fully {@link Property#mutable()} and throws {@link UnsupportedOperationException}
471 * @param dependingProps the array of {@link ComputedProperty derived properties}
472 * that depend on the value of the list
473 * @return new, empty list associated with this proto-object and its model
475 public <T> List<T> createList(String propName, int onChange, String... dependingProps) {
476 return new JSONList<T>(this, propName, onChange, dependingProps);
479 /** Copies content of one collection to another, re-assigning all its
480 * elements from their current context to the new <code>ctx</code>.
482 * @param <T> type of the collections
483 * @param to the target collection to be filled with cloned values
484 * @param ctx context for the new collection
485 * @param from original collection with its data
487 public <T> void cloneList(Collection<T> to, BrwsrCtx ctx, Collection<T> from) {
488 Boolean isModel = null;
490 if (isModel == null) {
491 isModel = JSON.isModel(t.getClass());
494 to.add(JSON.bindTo(t, ctx));
505 final String toStr() {
506 return "Proto[" + obj + "]@" + Integer.toHexString(System.identityHashCode(this));
509 final Bindings initBindings(Object originalObject) {
511 Bindings b = Bindings.apply(context);
512 PropertyBinding[] pb = new PropertyBinding[type.propertyNames.length];
513 for (int i = 0; i < pb.length; i++) {
514 pb[i] = b.registerProperty(
515 type.propertyNames[i], i, obj, type, type.propertyReadOnly[i]
518 FunctionBinding[] fb = new FunctionBinding[type.functions.length];
519 for (int i = 0; i < fb.length; i++) {
520 fb[i] = FunctionBinding.registerFunction(
521 type.functions[i], i, obj, type
525 b.finish(obj, originalObject, pb, fb);
530 final Bindings getBindings() {
534 final void onChange(int index) {
535 type.onChange(obj, index);
538 final Observers observers(boolean create) {
539 if (create && observers == null) {
540 observers = new Observers();
545 /** Functionality used by the code generated by annotation
546 * processor for the {@link net.java.html.json.Model} annotation.
548 * @param <Model> the generated class
551 public static abstract class Type<Model> {
552 private final Class<Model> clazz;
553 private final String[] propertyNames;
554 private final boolean[] propertyReadOnly;
555 private final String[] functions;
557 /** Constructor for subclasses generated by the annotation processor
558 * associated with {@link net.java.html.json.Model} annotation.
560 * @param clazz the generated model class
561 * @param modelFor the original class annotated by the {@link net.java.html.json.Model} annotation.
562 * @param properties number of properties the class has
563 * @param functions number of functions the class has
566 Class<Model> clazz, Class<?> modelFor, int properties, int functions
568 assert getClass().getName().endsWith("$Html4JavaType");
570 assert getClass().getDeclaringClass() == clazz;
571 } catch (SecurityException ex) {
575 this.propertyNames = new String[properties];
576 this.propertyReadOnly = new boolean[properties];
577 this.functions = new String[functions];
578 JSON.register(clazz, this);
581 /** Registers property for the type. It is expected each index
582 * is initialized only once.
584 * @param name name of the property
585 * @param index index of the property
586 * @param readOnly is the property read only?
588 protected final void registerProperty(String name, int index, boolean readOnly) {
589 assert propertyNames[index] == null;
590 propertyNames[index] = name;
591 propertyReadOnly[index] = readOnly;
594 /** Registers function of given name at given index.
596 * @param name name of the function
597 * @param index name of the type
599 protected final void registerFunction(String name, int index) {
600 assert functions[index] == null;
601 functions[index] = name;
604 /** Creates new proto-object for given {@link Model} class bound to
607 * @param obj instance of appropriate {@link Model} class
608 * @param context the browser context
609 * @return new proto-object that the generated class can use for
610 * communication with the infrastructure
612 public Proto createProto(Object obj, BrwsrCtx context) {
613 return new Proto(obj, this, context);
617 // Implemented by subclasses
620 /** Sets value of a {@link #registerProperty(java.lang.String, int, boolean) registered property}
623 * @param model the instance of {@link Model model class}
624 * @param index index of the property used during registration
625 * @param value the value to set the property to
627 protected abstract void setValue(Model model, int index, Object value);
629 /** Obtains and returns value of a
630 * {@link #registerProperty(java.lang.String, int, boolean) registered property}.
632 * @param model the instance of {@link Model model class}
633 * @param index index of the property used during registration
634 * @return current value of the property
636 protected abstract Object getValue(Model model, int index);
638 /** Invokes a {@link #registerFunction(java.lang.String, int)} registered function
641 * @param model the instance of {@link Model model class}
642 * @param index index of the property used during registration
643 * @param data the currently selected object the function is about to operate on
644 * @param event the event that triggered the event
645 * @throws Exception the method can throw exception which is then logged
647 protected abstract void call(Model model, int index, Object data, Object event)
650 /** Re-binds the model object to new browser context.
652 * @param model the instance of {@link Model model class}
653 * @param ctx browser context to clone the object to
654 * @return new instance of the model suitable for new context
656 protected abstract Model cloneTo(Model model, BrwsrCtx ctx);
658 /** Reads raw JSON data and converts them to our model class.
660 * @param c the browser context to work in
661 * @param json raw JSON data to get values from
662 * @return new instance of model class filled by the data
664 protected abstract Model read(BrwsrCtx c, Object json);
666 /** Called when a {@link #registerProperty(java.lang.String, int, boolean) registered property}
669 * @param model the object that has the property
670 * @param index the index of the property during registration
672 protected abstract void onChange(Model model, int index);
674 /** Finds out if there is an associated proto-object for given
677 * @param object an object, presumably (but not necessarily) instance of Model class
678 * @return associated proto-object or <code>null</code>
680 protected abstract Proto protoFor(Object object);
682 /** Called to report results of asynchronous over-the-wire
683 * communication. Result of calling {@link Proto#wsOpen(int, java.lang.String, java.lang.Object)}
684 * or {@link Proto#loadJSON(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object, java.lang.Object...)}.
686 * @param model the instance of the model class
687 * @param index index used during initiating the communication (via <code>loadJSON</code> or <code>wsOpen</code> calls)
688 * @param type type of the message: 0 - onOpen, 1 - onMessage, 2 - onError, 3 - onClose -
689 * not all messages are applicable to all communication protocols (JSON has only 1 and 2).
690 * @param data <code>null</code> or string, number or a {@link Model} class
691 * obtained to the server as a response
693 protected void onMessage(Model model, int index, int type, Object data) {
694 onMessage(model, index, type, data, new Object[0]);
697 /** Called to report results of asynchronous over-the-wire
698 * communication. Result of calling {@link Proto#wsOpen(int, java.lang.String, java.lang.Object)}
699 * or {@link Proto#loadJSON(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object, java.lang.Object...)}.
701 * @param model the instance of the model class
702 * @param index index used during initiating the communication (via <code>loadJSON</code> or <code>wsOpen</code> calls)
703 * @param type type of the message: 0 - onOpen, 1 - onMessage, 2 - onError, 3 - onClose -
704 * not all messages are applicable to all communication protocols (JSON has only 1 and 2).
705 * @param data <code>null</code> or string, number or a {@link Model} class
706 * obtained to the server as a response
707 * @param params extra parameters as passed for example to
708 * {@link Proto#loadJSON(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object, java.lang.Object...)}
712 protected void onMessage(Model model, int index, int type, Object data, Object[] params) {
713 onMessage(model, index, type, data);
717 // Various support methods the generated classes use
720 /** Converts and array of raw JSON objects into an array of typed
721 * Java {@link Model} classes.
723 * @param <T> the type of the destination array
724 * @param context browser context to use
725 * @param src array of raw JSON objects
726 * @param destType type of the individual array elements
727 * @param dest array to be filled with read type instances
729 public <T> void copyJSON(BrwsrCtx context, Object[] src, Class<T> destType, T[] dest) {
730 for (int i = 0; i < src.length && i < dest.length; i++) {
731 dest[i] = org.netbeans.html.json.impl.JSON.read(context, destType, src[i]);
735 /** Compares two objects that can be converted to integers.
736 * @param a first value
737 * @param b second value
738 * @return true if they are the same
740 public final boolean isSame(int a, int b) {
744 /** Compares two objects that can be converted to (floating point)
746 * @param a first value
747 * @param b second value
748 * @return true if they are the same
750 public final boolean isSame(double a, double b) {
754 /** Compares two objects for being the same - e.g. either <code>==</code>
755 * or <code>equals</code>.
756 * @param a first value
757 * @param b second value
758 * @return true if they are equals
760 public final boolean isSame(Object a, Object b) {
764 if (a == null || b == null) {
770 /** Cumulative hash function. Adds hashcode of the object to the
772 * @param o the object (or <code>null</code>)
773 * @param h the previous value of the hash
774 * @return new hash - the old one xor the object's one
776 public final int hashPlus(Object o, int h) {
777 return o == null ? h : h ^ o.hashCode();
780 /** Converts an object to its JSON value.
782 * @param obj the object to convert
783 * @return JSON representation of the object
785 public final String toJSON(Object obj) {
786 return JSON.toJSON(obj);
789 /** Converts the value to string.
791 * @param val the value
792 * @return the converted value
794 public final String stringValue(Object val) {
795 return JSON.stringValue(val);
798 /** Converts the value to number.
800 * @param val the value
801 * @return the converted value
803 public final Number numberValue(Object val) {
804 return JSON.numberValue(val);
807 /** Converts the value to character.
809 * @param val the value
810 * @return the converted value
812 public final Character charValue(Object val) {
813 return JSON.charValue(val);
816 /** Converts the value to boolean.
818 * @param val the value
819 * @return the converted value
821 public final Boolean boolValue(Object val) {
822 return JSON.boolValue(val);
825 /** Extracts value of specific type from given object.
827 * @param <T> the type of object one is interested in
828 * @param type the type
829 * @param val the object to convert to type
830 * @return the converted value
832 public final <T> T extractValue(Class<T> type, Object val) {
833 if (Number.class.isAssignableFrom(type)) {
834 val = numberValue(val);
836 if (Boolean.class == type) {
837 val = boolValue(val);
839 if (String.class == type) {
840 val = stringValue(val);
842 if (Character.class == type) {
843 val = charValue(val);
845 if (Integer.class == type) {
846 val = val instanceof Number ? ((Number) val).intValue() : 0;
848 if (Long.class == type) {
849 val = val instanceof Number ? ((Number) val).longValue() : 0;
851 if (Short.class == type) {
852 val = val instanceof Number ? ((Number) val).shortValue() : 0;
854 if (Byte.class == type) {
855 val = val instanceof Number ? ((Number) val).byteValue() : 0;
857 if (Double.class == type) {
858 val = val instanceof Number ? ((Number) val).doubleValue() : Double.NaN;
860 if (Float.class == type) {
861 val = val instanceof Number ? ((Number) val).floatValue() : Float.NaN;
863 if (type.isEnum() && val instanceof String) {
864 val = Enum.valueOf(type.asSubclass(Enum.class), (String)val);
866 return type.cast(val);
869 /** Special dealing with array & {@link List} values. This method
870 * takes the provided collection, empties it and fills it again
871 * with values extracted from <code>value</code> (which is supposed
874 * @param <T> the type of list elements
875 * @param arr collection to fill with elements in value
876 * @param type the type of elements in the collection
877 * @param value array of elements to put into the collecition. If
878 * value is not an array it is wrapped into array with only element
881 public final <T> void replaceValue(Collection<? super T> arr, Class<T> type, Object value) {
882 List<T> tmp = new ArrayList<T>();
883 if (value instanceof Object[]) {
884 for (Object e : (Object[]) value) {
885 tmp.add(extractValue(type, e));
887 } else if (value instanceof byte[]) {
888 for (Object e : (byte[]) value) {
889 tmp.add(extractValue(type, e));
891 } else if (value instanceof short[]) {
892 for (Object e : (short[]) value) {
893 tmp.add(extractValue(type, e));
895 } else if (value instanceof int[]) {
896 for (Object e : (int[]) value) {
897 tmp.add(extractValue(type, e));
899 } else if (value instanceof char[]) {
900 for (Object e : (char[]) value) {
901 tmp.add(extractValue(type, e));
903 } else if (value instanceof long[]) {
904 for (Object e : (long[]) value) {
905 tmp.add(extractValue(type, e));
907 } else if (value instanceof float[]) {
908 for (Object e : (float[]) value) {
909 tmp.add(extractValue(type, e));
911 } else if (value instanceof double[]) {
912 for (Object e : (double[]) value) {
913 tmp.add(extractValue(type, e));
916 tmp.add(extractValue(type, value));
918 if (arr instanceof JSONList) {
919 JSONList jsList = (JSONList) arr;
920 jsList.fastReplace(tmp);