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 org.netbeans.html.json.impl.Bindings;
52 import org.netbeans.html.json.impl.JSON;
53 import org.netbeans.html.json.impl.JSON.WS;
54 import org.netbeans.html.json.impl.JSONList;
55 import org.netbeans.html.json.impl.PropertyBindingAccessor;
56 import org.netbeans.html.json.impl.RcvrJSON;
57 import org.netbeans.html.json.impl.RcvrJSON.MsgEvnt;
59 /** Object associated with one instance of a model generated by the
60 * {@link Model} annotation. Contains methods the generated class can
61 * use to communicate with behind the scene associated {@link Technology}.
62 * Each {@link Proto} object is associated with <a href="http://wiki.apidesign.org/wiki/Singletonizer">
63 * singletonizer</a>-like interface {@link Type} which provides the
64 * associated {@link Technology} the necessary information about the
65 * generated {@link Model} class.
67 * @author Jaroslav Tulach
70 public final class Proto {
71 private final Object obj;
72 private final Type type;
73 private final net.java.html.BrwsrCtx context;
74 private org.netbeans.html.json.impl.Bindings ko;
75 private Observers observers;
77 Proto(Object obj, Type type, BrwsrCtx context) {
80 this.context = context;
83 /** Browser context this proto object and its associated model
86 * @return the associated context
88 public BrwsrCtx getContext() {
92 /** Acquires global lock to compute a {@link ComputedProperty derived property}
93 * on this proto object. This proto object must not be locked yet. No
94 * dependency tracking is performed.
96 * @throws IllegalStateException if already locked
98 public void acquireLock() throws IllegalStateException {
102 /** Acquires global lock to compute a {@link ComputedProperty derived property}
103 * on this proto object. This proto object must not be locked yet. The
104 * name of the property is used to track dependencies on own
105 * properties of other proto objects - when they are changed, this
106 * {@link #valueHasMutated(java.lang.String) property is changed too}.
108 * @param propName name of property we are about to compute
109 * @throws IllegalStateException thrown when there is a cyclic
113 public void acquireLock(String propName) throws IllegalStateException {
114 Observers.beginComputing(this, propName);
117 /** A property on this proto object is about to be accessed. Verifies
118 * whether this proto object is accessible - e.g. it has not been
119 * {@link #acquireLock() locked yet}. If everything is OK, the
120 * <code>propName</code> is recorded in the chain of dependencies
121 * tracked by {@link #acquireLock(java.lang.String)} and watched by
122 * {@link #valueHasMutated(java.lang.String)}.
124 * @param propName name of the property that is requested
125 * @throws IllegalStateException if the model is locked
128 public void accessProperty(String propName) throws IllegalStateException {
129 Observers.accessingValue(this, propName);
132 /** Verifies the model is not locked otherwise throws an exception.
133 * @throws IllegalStateException if the model is locked
135 public void verifyUnlocked() throws IllegalStateException {
136 Observers.verifyUnlocked(this);
139 /** When modifications are over, the model is switched into
140 * unlocked state by calling this method.
142 public void releaseLock() {
143 Observers.finishComputing(this);
146 /** Whenever model changes a property. It should notify the
147 * associated technology by calling this method.
148 * Since 0.8.3: This method may be called by any thread - it reschedules
149 * its actual execution into appropriate one by using
150 * {@link BrwsrCtx#execute(java.lang.Runnable)}.
152 * @param propName name of the changed property
154 public void valueHasMutated(final String propName) {
155 context.execute(new Runnable() {
159 ko.valueHasMutated(propName, null, null);
161 Observers.valueHasMutated(Proto.this, propName);
166 /** Whenever model changes a propertyit should notify the
167 * associated technology. Either by calling this method
168 * (if the new value is known and different to the old one) or
169 * via (slightly ineffective) {@link #valueHasMutated(java.lang.String)}
171 * Since 0.8.3: This method may be called by any thread - it reschedules
172 * its actual execution into appropriate one by using
173 * {@link BrwsrCtx#execute(java.lang.Runnable)}.
175 * @param propName name of the changed property
176 * @param oldValue provides previous value of the property
177 * @param newValue provides new value of the property
180 public void valueHasMutated(
181 final String propName, final Object oldValue, final Object newValue
183 context.execute(new Runnable() {
187 ko.valueHasMutated(propName, oldValue, newValue);
189 Observers.valueHasMutated(Proto.this, propName);
194 /** Initializes the associated model in the current {@link #getContext() context}.
195 * In case of <em>knockout.js</em> technology, applies given bindings
196 * of the current model to the <em>body</em> element of the page.
198 public void applyBindings() {
199 initBindings().applyBindings(null);
202 /** Initializes the associated model to the specified element's subtree.
203 * The technology is taken from the current {@link #getContext() context} and
204 * in case of <em>knockout.js</em> applies given bindings
205 * of the current model to the element of the page with 'id' attribute
206 * set to the specified <code>id</code> value.
208 * @param id the id of element to apply the binding to
210 * @see Technology.ApplyId
212 public void applyBindings(String id) {
213 initBindings().applyBindings(id);
216 /** Invokes the provided runnable in the {@link #getContext() context}
217 * of the browser. If the caller is already on the right thread, the
218 * <code>run.run()</code> is invoked immediately and synchronously.
219 * Otherwise the method returns immediately and the <code>run()</code>
220 * method is performed later
222 * @param run the action to execute
224 public void runInBrowser(Runnable run) {
225 context.execute(run);
228 /** Invokes the specified function index in the {@link #getContext() context}
229 * of the browser. If the caller is already on the right thread, the
230 * index-th function is invoked immediately and synchronously.
231 * Otherwise the method returns immediately and the function is invoked
234 * @param index the index of the function as will be passed to
235 * {@link Type#call(java.lang.Object, int, java.lang.Object, java.lang.Object)}
237 * @param args array of arguments that will be passed as
238 * <code>data</code> argument of the <code>call</code> method.
241 public void runInBrowser(final int index, final Object... args) {
242 context.execute(new Runnable() {
246 type.call(obj, index, args, null);
247 } catch (Exception ex) {
248 ex.printStackTrace();
254 /** Initializes the provided collection with a content of the <code>array</code>.
255 * The initialization can only be done soon after the the collection
256 * is created, otherwise an exception is throw
258 * @param to the collection to initialize (assumed to be empty)
259 * @param array the array to add to the collection
260 * @throws IllegalStateException if the system has already been initialized
262 public void initTo(Collection<?> to, Object array) {
264 throw new IllegalStateException();
266 if (to instanceof JSONList) {
267 ((JSONList)to).init(array);
269 JSONList.init(to, array);
273 /** Takes an object representing JSON result and extract some of its
274 * properties. It is assumed that the <code>props</code> and
275 * <code>values</code> arrays have the same length.
277 * @param json the JSON object (actual type depends on the associated
278 * {@link Technology})
279 * @param props list of properties to extract
280 * @param values array that will be filled with extracted values
282 public void extract(Object json, String[] props, Object[] values) {
283 JSON.extract(context, json, props, values);
286 /** Converts raw JSON <code>data</code> into a Java {@link Model} class.
288 * @param <T> type of the model class
289 * @param modelClass the type of the class to create
290 * @param data the raw JSON data
291 * @return newly created instance of the model class
293 public <T> T read(Class<T> modelClass, Object data) {
294 return JSON.read(context, modelClass, data);
297 /** Initializes asynchronous JSON connection to specified URL. Delegates
298 * to {@link #loadJSON(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object, java.lang.Object...) }
299 * with no extra parameters.
301 * @param index the callback index to be used when a reply is received
302 * to call {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object)}.
304 * @param urlBefore the part of the URL before JSON-P callback parameter
305 * @param urlAfter the rest of the URL or <code>null</code> if no JSON-P is used
306 * @param method method to use for connection to the server
307 * @param data string, number or a {@link Model} generated class to send to
308 * the server when doing a query
310 public void loadJSON(final int index,
311 String urlBefore, String urlAfter, String method,
314 loadJSON(index, urlBefore, urlAfter, method, data, new Object[0]);
317 /** Initializes asynchronous JSON connection to specified URL. The
318 * method returns immediately and later does callback later.
320 * @param index the callback index to be used when a reply is received
321 * to call {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object)}.
323 * @param urlBefore the part of the URL before JSON-P callback parameter
324 * @param urlAfter the rest of the URL or <code>null</code> if no JSON-P is used
325 * @param method method to use for connection to the server
326 * @param data string, number or a {@link Model} generated class to send to
327 * the server when doing a query
328 * @param params extra params to pass back when calling
329 * {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object, java.lang.Object[])}
332 public void loadJSON(final int index,
333 String urlBefore, String urlAfter, String method,
334 final Object data, final Object... params
336 loadJSONWithHeaders(index, null, urlBefore, urlAfter, method, data, params);
339 /** Initializes asynchronous JSON connection to specified URL. The
340 * method returns immediately and later does callback later.
342 * @param index the callback index to be used when a reply is received
343 * to call {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object)}.
345 * @param headers headers to use for the request or <code>null</code> to use default ones
346 * @param urlBefore the part of the URL before JSON-P callback parameter
347 * @param urlAfter the rest of the URL or <code>null</code> if no JSON-P is used
348 * @param method method to use for connection to the server
349 * @param data string, number or a {@link Model} generated class to send to
350 * the server when doing a query
351 * @param params extra params to pass back when calling
352 * {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object, java.lang.Object[])}
355 public void loadJSONWithHeaders(final int index,
357 String urlBefore, String urlAfter, String method,
358 final Object data, final Object... params
360 class Rcvr extends RcvrJSON {
362 protected void onMessage(MsgEvnt msg) {
363 type.onMessage(obj, index, 1, msg.getValues(), params);
367 protected void onError(MsgEvnt msg) {
368 type.onMessage(obj, index, 2, msg.getException(), params);
371 JSONCall call = PropertyBindingAccessor.createCall(
372 context, new Rcvr(), headers, urlBefore, urlAfter, method, data
374 Transfer t = JSON.findTransfer(context);
378 /** Opens new WebSocket connection to the specified URL.
380 * @param index the index to use later during callbacks to
381 * {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object)}
382 * @param url the <code>ws://</code> or <code>wss://</code> URL to connect to
383 * @param data data to send to server (usually <code>null</code>)
384 * @return returns a non-null object representing the socket
385 * which can be used when calling {@link #wsSend(java.lang.Object, java.lang.String, java.lang.Object) }
387 public Object wsOpen(final int index, String url, Object data) {
388 class WSrcvr extends RcvrJSON {
390 protected void onError(MsgEvnt msg) {
391 type.onMessage(obj, index, 2, msg.getException());
395 protected void onMessage(MsgEvnt msg) {
396 type.onMessage(obj, index, 1, msg.getValues());
400 protected void onClose(MsgEvnt msg) {
401 type.onMessage(obj, index, 3, null);
405 protected void onOpen(MsgEvnt msg) {
406 type.onMessage(obj, index, 0, null);
409 WS ws = WS.create(JSON.findWSTransfer(context), new WSrcvr());
410 ws.send(context, null, url, data);
414 /** Sends a message to existing socket.
416 * @param webSocket the socket to send message to
417 * @param url the <code>ws://</code> or <code>wss://</code> URL to connect to,
418 * preferably the same as the one used when the socket was
419 * {@link #wsOpen(int, java.lang.String, java.lang.Object) opened}
420 * @param data the data to send or <code>null</code> if the socket is
421 * supposed to be closed
423 public void wsSend(Object webSocket, String url, Object data) {
424 ((JSON.WS)webSocket).send(context, null, url, data);
427 /** Converts raw data (one of its properties) to string representation.
429 * @param data the object
430 * @param propName the name of object property or <code>null</code>
431 * if the whole object should be converted
432 * @return the string representation of the object or its property
434 public String toString(Object data, String propName) {
435 return JSON.toString(context, data, propName);
438 /** Converts raw data (one of its properties) to a number representation.
440 * @param data the object
441 * @param propName the name of object property or <code>null</code>
442 * if the whole object should be converted
443 * @return the number representation of the object or its property
445 public Number toNumber(Object data, String propName) {
446 return JSON.toNumber(context, data, propName);
449 /** Converts raw JSON data into a {@link Model} class representation.
451 * @param <T> type of the model to create
452 * @param type class of the model to create
453 * @param data raw JSON data (depends on associated {@link Technology})
454 * @return new instances of the model class filled with values from the
455 * <code>data</code> object
457 public <T> T toModel(Class<T> type, Object data) {
458 return JSON.toModel(context, type, data, null);
461 /** Creates new JSON like observable list.
463 * @param <T> the type of the list elements
464 * @param propName name of a property this list is associated with
465 * @param onChange index of the property to use when the list is modified
466 * during callback to {@link Type#onChange(java.lang.Object, int)}
467 * @param dependingProps the array of {@link ComputedProperty derived properties}
468 * that depend on the value of the list
469 * @return new, empty list associated with this proto-object and its model
471 public <T> List<T> createList(String propName, int onChange, String... dependingProps) {
472 return new JSONList<T>(this, propName, onChange, dependingProps);
475 /** Copies content of one collection to another, re-assigning all its
476 * elements from their current context to the new <code>ctx</code>.
478 * @param <T> type of the collections
479 * @param to the target collection to be filled with cloned values
480 * @param ctx context for the new collection
481 * @param from original collection with its data
483 public <T> void cloneList(Collection<T> to, BrwsrCtx ctx, Collection<T> from) {
484 Boolean isModel = null;
486 if (isModel == null) {
487 isModel = JSON.isModel(t.getClass());
490 to.add(JSON.bindTo(t, ctx));
501 final String toStr() {
502 return "Proto[" + obj + "]@" + Integer.toHexString(System.identityHashCode(this));
505 final Bindings initBindings() {
507 Bindings b = Bindings.apply(context, obj);
508 PropertyBinding[] pb = new PropertyBinding[type.propertyNames.length];
509 for (int i = 0; i < pb.length; i++) {
510 pb[i] = b.registerProperty(
511 type.propertyNames[i], i, obj, type, type.propertyReadOnly[i]
514 FunctionBinding[] fb = new FunctionBinding[type.functions.length];
515 for (int i = 0; i < fb.length; i++) {
516 fb[i] = FunctionBinding.registerFunction(
517 type.functions[i], i, obj, type
521 b.finish(obj, pb, fb);
526 final Bindings getBindings() {
530 final void onChange(int index) {
531 type.onChange(obj, index);
534 final Observers observers(boolean create) {
535 if (create && observers == null) {
536 observers = new Observers();
541 /** Functionality used by the code generated by annotation
542 * processor for the {@link net.java.html.json.Model} annotation.
544 * @param <Model> the generated class
547 public static abstract class Type<Model> {
548 private final Class<Model> clazz;
549 private final String[] propertyNames;
550 private final boolean[] propertyReadOnly;
551 private final String[] functions;
553 /** Constructor for subclasses generated by the annotation processor
554 * associated with {@link net.java.html.json.Model} annotation.
556 * @param clazz the generated model class
557 * @param modelFor the original class annotated by the {@link net.java.html.json.Model} annotation.
558 * @param properties number of properties the class has
559 * @param functions number of functions the class has
562 Class<Model> clazz, Class<?> modelFor, int properties, int functions
564 assert getClass().getName().endsWith("$Html4JavaType");
566 assert clazz == modelFor || getClass().getDeclaringClass() == clazz;
567 } catch (SecurityException ex) {
571 this.propertyNames = new String[properties];
572 this.propertyReadOnly = new boolean[properties];
573 this.functions = new String[functions];
574 JSON.register(clazz, this);
577 /** Registers property for the type. It is expected each index
578 * is initialized only once.
580 * @param name name of the property
581 * @param index index of the property
582 * @param readOnly is the property read only?
584 protected final void registerProperty(String name, int index, boolean readOnly) {
585 assert propertyNames[index] == null;
586 propertyNames[index] = name;
587 propertyReadOnly[index] = readOnly;
590 /** Registers function of given name at given index.
592 * @param name name of the function
593 * @param index name of the type
595 protected final void registerFunction(String name, int index) {
596 assert functions[index] == null;
597 functions[index] = name;
600 /** Creates new proto-object for given {@link Model} class bound to
603 * @param obj instance of appropriate {@link Model} class
604 * @param context the browser context
605 * @return new proto-object that the generated class can use for
606 * communication with the infrastructure
608 public Proto createProto(Object obj, BrwsrCtx context) {
609 return new Proto(obj, this, context);
613 // Implemented by subclasses
616 /** Sets value of a {@link #registerProperty(java.lang.String, int, boolean) registered property}
619 * @param model the instance of {@link Model model class}
620 * @param index index of the property used during registration
621 * @param value the value to set the property to
623 protected abstract void setValue(Model model, int index, Object value);
625 /** Obtains and returns value of a
626 * {@link #registerProperty(java.lang.String, int, boolean) registered property}.
628 * @param model the instance of {@link Model model class}
629 * @param index index of the property used during registration
630 * @return current value of the property
632 protected abstract Object getValue(Model model, int index);
634 /** Invokes a {@link #registerFunction(java.lang.String, int)} registered function
637 * @param model the instance of {@link Model model class}
638 * @param index index of the property used during registration
639 * @param data the currently selected object the function is about to operate on
640 * @param event the event that triggered the event
641 * @throws Exception the method can throw exception which is then logged
643 protected abstract void call(Model model, int index, Object data, Object event)
646 /** Re-binds the model object to new browser context.
648 * @param model the instance of {@link Model model class}
649 * @param ctx browser context to clone the object to
650 * @return new instance of the model suitable for new context
652 protected abstract Model cloneTo(Model model, BrwsrCtx ctx);
654 /** Reads raw JSON data and converts them to our model class.
656 * @param c the browser context to work in
657 * @param json raw JSON data to get values from
658 * @return new instance of model class filled by the data
660 protected abstract Model read(BrwsrCtx c, Object json);
662 /** Called when a {@link #registerProperty(java.lang.String, int, boolean) registered property}
665 * @param model the object that has the property
666 * @param index the index of the property during registration
668 protected abstract void onChange(Model model, int index);
670 /** Finds out if there is an associated proto-object for given
673 * @param object an object, presumably (but not necessarily) instance of Model class
674 * @return associated proto-object or <code>null</code>
676 protected abstract Proto protoFor(Object object);
678 /** Called to report results of asynchronous over-the-wire
679 * communication. Result of calling {@link Proto#wsOpen(int, java.lang.String, java.lang.Object)}
680 * or {@link Proto#loadJSON(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object, java.lang.Object...)}.
682 * @param model the instance of the model class
683 * @param index index used during initiating the communication (via <code>loadJSON</code> or <code>wsOpen</code> calls)
684 * @param type type of the message: 0 - onOpen, 1 - onMessage, 2 - onError, 3 - onClose -
685 * not all messages are applicable to all communication protocols (JSON has only 1 and 2).
686 * @param data <code>null</code> or string, number or a {@link Model} class
687 * obtained to the server as a response
689 protected void onMessage(Model model, int index, int type, Object data) {
690 onMessage(model, index, type, data, new Object[0]);
693 /** Called to report results of asynchronous over-the-wire
694 * communication. Result of calling {@link Proto#wsOpen(int, java.lang.String, java.lang.Object)}
695 * or {@link Proto#loadJSON(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object, java.lang.Object...)}.
697 * @param model the instance of the model class
698 * @param index index used during initiating the communication (via <code>loadJSON</code> or <code>wsOpen</code> calls)
699 * @param type type of the message: 0 - onOpen, 1 - onMessage, 2 - onError, 3 - onClose -
700 * not all messages are applicable to all communication protocols (JSON has only 1 and 2).
701 * @param data <code>null</code> or string, number or a {@link Model} class
702 * obtained to the server as a response
703 * @param params extra parameters as passed for example to
704 * {@link Proto#loadJSON(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object, java.lang.Object...)}
708 protected void onMessage(Model model, int index, int type, Object data, Object[] params) {
709 onMessage(model, index, type, data);
713 // Various support methods the generated classes use
716 /** Converts and array of raw JSON objects into an array of typed
717 * Java {@link Model} classes.
719 * @param <T> the type of the destination array
720 * @param context browser context to use
721 * @param src array of raw JSON objects
722 * @param destType type of the individual array elements
723 * @param dest array to be filled with read type instances
725 public <T> void copyJSON(BrwsrCtx context, Object[] src, Class<T> destType, T[] dest) {
726 for (int i = 0; i < src.length && i < dest.length; i++) {
727 dest[i] = org.netbeans.html.json.impl.JSON.read(context, destType, src[i]);
731 /** Compares two objects that can be converted to integers.
732 * @param a first value
733 * @param b second value
734 * @return true if they are the same
736 public final boolean isSame(int a, int b) {
740 /** Compares two objects that can be converted to (floating point)
742 * @param a first value
743 * @param b second value
744 * @return true if they are the same
746 public final boolean isSame(double a, double b) {
750 /** Compares two objects for being the same - e.g. either <code>==</code>
751 * or <code>equals</code>.
752 * @param a first value
753 * @param b second value
754 * @return true if they are equals
756 public final boolean isSame(Object a, Object b) {
760 if (a == null || b == null) {
766 /** Cumulative hash function. Adds hashcode of the object to the
768 * @param o the object (or <code>null</code>)
769 * @param h the previous value of the hash
770 * @return new hash - the old one xor the object's one
772 public final int hashPlus(Object o, int h) {
773 return o == null ? h : h ^ o.hashCode();
776 /** Converts an object to its JSON value.
778 * @param obj the object to convert
779 * @return JSON representation of the object
781 public final String toJSON(Object obj) {
782 return JSON.toJSON(obj);
785 /** Converts the value to string.
787 * @param val the value
788 * @return the converted value
790 public final String stringValue(Object val) {
791 return JSON.stringValue(val);
794 /** Converts the value to number.
796 * @param val the value
797 * @return the converted value
799 public final Number numberValue(Object val) {
800 return JSON.numberValue(val);
803 /** Converts the value to character.
805 * @param val the value
806 * @return the converted value
808 public final Character charValue(Object val) {
809 return JSON.charValue(val);
812 /** Converts the value to boolean.
814 * @param val the value
815 * @return the converted value
817 public final Boolean boolValue(Object val) {
818 return JSON.boolValue(val);
821 /** Extracts value of specific type from given object.
823 * @param <T> the type of object one is interested in
824 * @param type the type
825 * @param val the object to convert to type
826 * @return the converted value
828 public final <T> T extractValue(Class<T> type, Object val) {
829 if (Number.class.isAssignableFrom(type)) {
830 val = numberValue(val);
832 if (Boolean.class == type) {
833 val = boolValue(val);
835 if (String.class == type) {
836 val = stringValue(val);
838 if (Character.class == type) {
839 val = charValue(val);
841 if (Integer.class == type) {
842 val = val instanceof Number ? ((Number) val).intValue() : 0;
844 if (Long.class == type) {
845 val = val instanceof Number ? ((Number) val).longValue() : 0;
847 if (Short.class == type) {
848 val = val instanceof Number ? ((Number) val).shortValue() : 0;
850 if (Byte.class == type) {
851 val = val instanceof Number ? ((Number) val).byteValue() : 0;
853 if (Double.class == type) {
854 val = val instanceof Number ? ((Number) val).doubleValue() : Double.NaN;
856 if (Float.class == type) {
857 val = val instanceof Number ? ((Number) val).floatValue() : Float.NaN;
859 if (type.isEnum() && val instanceof String) {
860 val = Enum.valueOf(type.asSubclass(Enum.class), (String)val);
862 return type.cast(val);
865 /** Special dealing with array & {@link List} values. This method
866 * takes the provided collection, empties it and fills it again
867 * with values extracted from <code>value</code> (which is supposed
870 * @param <T> the type of list elements
871 * @param arr collection to fill with elements in value
872 * @param type the type of elements in the collection
873 * @param value array of elements to put into the collecition. If
874 * value is not an array it is wrapped into array with only element
877 public final <T> void replaceValue(Collection<? super T> arr, Class<T> type, Object value) {
879 if (value instanceof Object[]) {
880 newArr = (Object[]) value;
882 newArr = new Object[] { value };
884 List<T> tmp = new ArrayList<T>(newArr.length);
885 for (Object e : newArr) {
886 tmp.add(extractValue(type, e));
888 if (arr instanceof JSONList) {
889 JSONList jsList = (JSONList) arr;
890 jsList.fastReplace(tmp);