2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4 * Copyright 1997-2010 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-2013 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.impl;
45 import java.io.IOException;
46 import java.io.InputStream;
47 import java.util.HashMap;
49 import net.java.html.BrwsrCtx;
50 import org.apidesign.html.context.spi.Contexts;
51 import org.apidesign.html.json.spi.FunctionBinding;
52 import org.apidesign.html.json.spi.JSONCall;
53 import org.apidesign.html.json.spi.PropertyBinding;
54 import org.apidesign.html.json.spi.Technology;
55 import org.apidesign.html.json.spi.Transfer;
56 import org.apidesign.html.json.spi.WSTransfer;
60 * @author Jaroslav Tulach <jtulach@netbeans.org>
62 public final class JSON {
66 static Technology<?> findTechnology(BrwsrCtx c) {
67 Technology<?> t = Contexts.find(c, Technology.class);
68 return t == null ? EmptyTech.EMPTY : t;
71 static Transfer findTransfer(BrwsrCtx c) {
72 Transfer t = Contexts.find(c, Transfer.class);
73 return t == null ? EmptyTech.EMPTY : t;
76 static WSTransfer<?> findWSTransfer(BrwsrCtx c) {
77 WSTransfer<?> t = Contexts.find(c, WSTransfer.class);
78 return t == null ? EmptyTech.EMPTY : t;
81 public static void runInBrowser(BrwsrCtx c, Runnable runnable) {
82 findTechnology(c).runSafe(runnable);
85 public static void extract(BrwsrCtx c, Object value, String[] props, Object[] values) {
86 Transfer t = findTransfer(c);
87 t.extract(value, props, values);
90 private static Object getProperty(BrwsrCtx c, Object obj, String prop) {
91 if (prop == null) return obj;
93 String[] arr = { prop };
94 Object[] val = { null };
95 extract(c, obj, arr, val);
99 public static Object toJSON(Object value) {
103 if (value instanceof Enum) {
104 value = value.toString();
106 if (value instanceof String) {
107 String s = (String)value;
108 int len = s.length();
109 StringBuilder sb = new StringBuilder(len + 10);
111 for (int i = 0; i < len; i++) {
112 char ch = s.charAt(i);
114 case '\"': sb.append("\\\""); break;
115 case '\n': sb.append("\\n"); break;
116 case '\r': sb.append("\\r"); break;
117 case '\t': sb.append("\\t"); break;
118 case '\\': sb.append("\\\\"); break;
119 default: sb.append(ch);
123 return sb.toString();
125 return value.toString();
128 public static String toString(BrwsrCtx c, Object obj, String prop) {
129 obj = getProperty(c, obj, prop);
130 return obj instanceof String ? (String)obj : null;
132 public static Number toNumber(BrwsrCtx c, Object obj, String prop) {
133 obj = getProperty(c, obj, prop);
134 if (!(obj instanceof Number)) {
139 public static <M> M toModel(BrwsrCtx c, Class<M> aClass, Object data, Object object) {
140 Technology<?> t = findTechnology(c);
141 Object o = t.toModel(aClass, data);
142 return aClass.cast(o);
145 public static boolean isSame(int a, int b) {
149 public static boolean isSame(double a, double b) {
153 public static boolean isSame(Object a, Object b) {
157 if (a == null || b == null) {
163 public static int hashPlus(Object o, int h) {
164 return o == null ? h : h ^ o.hashCode();
167 public static <T> T extractValue(Class<T> type, Object val) {
168 if (Number.class.isAssignableFrom(type)) {
169 val = numberValue(val);
171 if (Boolean.class == type) {
172 val = boolValue(val);
174 if (String.class == type) {
175 val = stringValue(val);
177 if (Character.class == type) {
178 val = charValue(val);
180 if (Integer.class == type) {
181 val = val instanceof Number ? ((Number)val).intValue() : 0;
183 if (Long.class == type) {
184 val = val instanceof Number ? ((Number)val).longValue() : 0;
186 if (Short.class == type) {
187 val = val instanceof Number ? ((Number)val).shortValue() : 0;
189 if (Byte.class == type) {
190 val = val instanceof Number ? ((Number)val).byteValue() : 0;
192 if (Double.class == type) {
193 val = val instanceof Number ? ((Number)val).doubleValue() : Double.NaN;
195 if (Float.class == type) {
196 val = val instanceof Number ? ((Number)val).floatValue() : Float.NaN;
198 return type.cast(val);
201 protected static boolean isNumeric(Object val) {
202 return ((val instanceof Integer) || (val instanceof Long) || (val instanceof Short) || (val instanceof Byte));
205 public static String stringValue(Object val) {
206 if (val instanceof Boolean) {
207 return ((Boolean)val ? "true" : "false");
209 if (isNumeric(val)) {
210 return Long.toString(((Number)val).longValue());
212 if (val instanceof Float) {
213 return Float.toString((Float)val);
215 if (val instanceof Double) {
216 return Double.toString((Double)val);
221 public static Number numberValue(Object val) {
222 if (val instanceof String) {
224 return Double.valueOf((String)val);
225 } catch (NumberFormatException ex) {
229 if (val instanceof Boolean) {
230 return (Boolean)val ? 1 : 0;
235 public static Character charValue(Object val) {
236 if (val instanceof Number) {
237 return Character.toChars(numberValue(val).intValue())[0];
239 if (val instanceof Boolean) {
240 return (Boolean)val ? (char)1 : (char)0;
242 if (val instanceof String) {
243 String s = (String)val;
244 return s.isEmpty() ? (char)0 : s.charAt(0);
246 return (Character)val;
249 public static Boolean boolValue(Object val) {
250 if (val instanceof String) {
251 return Boolean.parseBoolean((String)val);
253 if (val instanceof Number) {
254 return numberValue(val).doubleValue() != 0.0;
257 return Boolean.TRUE.equals(val);
260 public static void loadJSON(
261 BrwsrCtx c, RcvrJSON callback,
262 String urlBefore, String urlAfter, String method,
265 JSONCall call = PropertyBindingAccessor.createCall(c, callback, urlBefore, urlAfter, method, data);
266 Transfer t = findTransfer(c);
269 public static WS openWS(
270 BrwsrCtx c, RcvrJSON r, String url, Object data
272 WS ws = WSImpl.create(findWSTransfer(c), r);
273 ws.send(c, url, data);
277 public static abstract class WS {
281 public abstract void send(BrwsrCtx ctx, String url, Object model);
284 private static final class WSImpl<Socket> extends WS {
286 private final WSTransfer<Socket> trans;
287 private final RcvrJSON rcvr;
288 private Socket socket;
289 private String prevURL;
291 private WSImpl(WSTransfer<Socket> trans, RcvrJSON rcvr) {
296 static <Socket> WS create(WSTransfer<Socket> t, RcvrJSON r) {
297 return new WSImpl<Socket>(t, r);
301 public void send(BrwsrCtx ctx, String url, Object data) {
305 throw new IllegalStateException("WebSocket is not opened yet. Call with null data, was: " + data);
307 JSONCall call = PropertyBindingAccessor.createCall(ctx, rcvr, url, null, "WebSocket", null);
308 socket = trans.open(url, call);
317 if (!prevURL.equals(url)) {
318 throw new IllegalStateException(
319 "Can't call to different URL " + url + " was: " + prevURL + "!"
320 + " Close the socket by calling it will null data first!"
323 JSONCall call = PropertyBindingAccessor.createCall(ctx, rcvr, prevURL, null, "WebSocket", data);
329 private static final Map<Class,FromJSON<?>> froms;
331 Map<Class,FromJSON<?>> m = new HashMap<Class,FromJSON<?>>();
334 public static void register(FromJSON<?> from) {
335 froms.put(from.factoryFor(), from);
338 public static boolean isModel(Class<?> clazz) {
339 return findFrom(clazz) != null;
342 private static FromJSON<?> findFrom(Class<?> clazz) {
343 for (int i = 0; i < 2; i++) {
344 FromJSON<?> from = froms.get(clazz);
354 public static <Model> Model bindTo(Model model, BrwsrCtx c) {
355 FromJSON<?> from = findFrom(model.getClass());
357 throw new IllegalArgumentException();
359 return (Model) from.cloneTo(model, c);
362 public static <T> T readStream(BrwsrCtx c, Class<T> modelClazz, InputStream data)
364 Transfer tr = findTransfer(c);
365 return read(c, modelClazz, tr.toJSON((InputStream)data));
367 public static <T> T read(BrwsrCtx c, Class<T> modelClazz, Object data) {
371 if (modelClazz == String.class) {
372 return modelClazz.cast(data.toString());
374 for (int i = 0; i < 2; i++) {
375 FromJSON<?> from = froms.get(modelClazz);
377 initClass(modelClazz);
379 return modelClazz.cast(from.read(c, data));
382 throw new NullPointerException();
384 static void initClass(Class<?> modelClazz) {
386 // try to resolve the class
389 l = modelClazz.getClassLoader();
390 } catch (SecurityException ex) {
394 Class.forName(modelClazz.getName(), true, l);
396 modelClazz.newInstance();
397 } catch (Exception ex) {
398 // ignore and try again
402 private static final class EmptyTech
403 implements Technology<Object>, Transfer, WSTransfer<Void> {
404 private static final EmptyTech EMPTY = new EmptyTech();
407 public Object wrapModel(Object model) {
412 public void valueHasMutated(Object data, String propertyName) {
416 public void bind(PropertyBinding b, Object model, Object data) {
420 public void expose(FunctionBinding fb, Object model, Object d) {
424 public void applyBindings(Object data) {
428 public Object wrapArray(Object[] arr) {
433 public void extract(Object obj, String[] props, Object[] values) {
434 for (int i = 0; i < values.length; i++) {
440 public void loadJSON(JSONCall call) {
441 call.notifyError(new UnsupportedOperationException());
445 public <M> M toModel(Class<M> modelClass, Object data) {
446 return modelClass.cast(data);
450 public Object toJSON(InputStream is) throws IOException {
451 throw new IOException("Not supported");
455 public synchronized void runSafe(Runnable r) {
460 public Void open(String url, JSONCall onReply) {
461 onReply.notifyError(new UnsupportedOperationException("WebSockets not supported!"));
466 public void send(Void socket, JSONCall data) {
470 public void close(Void socket) {