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.impl;
45 import java.io.IOException;
46 import java.io.InputStream;
47 import java.util.Collection;
48 import java.util.HashMap;
50 import net.java.html.BrwsrCtx;
51 import org.apidesign.html.context.spi.Contexts;
52 import org.apidesign.html.json.spi.FunctionBinding;
53 import org.apidesign.html.json.spi.JSONCall;
54 import org.apidesign.html.json.spi.PropertyBinding;
55 import org.apidesign.html.json.spi.Proto;
56 import org.apidesign.html.json.spi.Technology;
57 import org.apidesign.html.json.spi.Transfer;
58 import org.apidesign.html.json.spi.WSTransfer;
62 * @author Jaroslav Tulach <jtulach@netbeans.org>
64 public final class JSON {
68 static Technology<?> findTechnology(BrwsrCtx c) {
69 Technology<?> t = Contexts.find(c, Technology.class);
70 return t == null ? EmptyTech.EMPTY : t;
73 static Transfer findTransfer(BrwsrCtx c) {
74 Transfer t = Contexts.find(c, Transfer.class);
75 return t == null ? EmptyTech.EMPTY : t;
78 static WSTransfer<?> findWSTransfer(BrwsrCtx c) {
79 WSTransfer<?> t = Contexts.find(c, WSTransfer.class);
80 return t == null ? EmptyTech.EMPTY : t;
83 public static void runInBrowser(BrwsrCtx c, Runnable runnable) {
84 findTechnology(c).runSafe(runnable);
87 public static void extract(BrwsrCtx c, Object value, String[] props, Object[] values) {
88 Transfer t = findTransfer(c);
89 t.extract(value, props, values);
92 private static Object getProperty(BrwsrCtx c, Object obj, String prop) {
93 if (prop == null) return obj;
95 String[] arr = { prop };
96 Object[] val = { null };
97 extract(c, obj, arr, val);
101 public static String toJSON(Object value) {
105 if (value instanceof Enum) {
106 value = value.toString();
108 if (value instanceof String) {
109 String s = (String)value;
110 int len = s.length();
111 StringBuilder sb = new StringBuilder(len + 10);
113 for (int i = 0; i < len; i++) {
114 char ch = s.charAt(i);
116 case '\"': sb.append("\\\""); break;
117 case '\n': sb.append("\\n"); break;
118 case '\r': sb.append("\\r"); break;
119 case '\t': sb.append("\\t"); break;
120 case '\\': sb.append("\\\\"); break;
121 default: sb.append(ch);
125 return sb.toString();
127 return value.toString();
130 public static String toString(BrwsrCtx c, Object obj, String prop) {
131 obj = getProperty(c, obj, prop);
132 return obj instanceof String ? (String)obj : null;
134 public static Number toNumber(BrwsrCtx c, Object obj, String prop) {
135 obj = getProperty(c, obj, prop);
136 if (!(obj instanceof Number)) {
141 public static <M> M toModel(BrwsrCtx c, Class<M> aClass, Object data, Object object) {
142 Technology<?> t = findTechnology(c);
143 Object o = t.toModel(aClass, data);
144 return aClass.cast(o);
147 public static boolean isSame(int a, int b) {
151 public static boolean isSame(double a, double b) {
155 public static boolean isSame(Object a, Object b) {
159 if (a == null || b == null) {
165 public static int hashPlus(Object o, int h) {
166 return o == null ? h : h ^ o.hashCode();
169 public static <T> T extractValue(Class<T> type, Object val) {
170 if (Number.class.isAssignableFrom(type)) {
171 val = numberValue(val);
173 if (Boolean.class == type) {
174 val = boolValue(val);
176 if (String.class == type) {
177 val = stringValue(val);
179 if (Character.class == type) {
180 val = charValue(val);
182 if (Integer.class == type) {
183 val = val instanceof Number ? ((Number)val).intValue() : 0;
185 if (Long.class == type) {
186 val = val instanceof Number ? ((Number)val).longValue() : 0;
188 if (Short.class == type) {
189 val = val instanceof Number ? ((Number)val).shortValue() : 0;
191 if (Byte.class == type) {
192 val = val instanceof Number ? ((Number)val).byteValue() : 0;
194 if (Double.class == type) {
195 val = val instanceof Number ? ((Number)val).doubleValue() : Double.NaN;
197 if (Float.class == type) {
198 val = val instanceof Number ? ((Number)val).floatValue() : Float.NaN;
200 return type.cast(val);
203 static boolean isNumeric(Object val) {
204 return ((val instanceof Integer) || (val instanceof Long) || (val instanceof Short) || (val instanceof Byte));
207 public static String stringValue(Object val) {
208 if (val instanceof Boolean) {
209 return ((Boolean)val ? "true" : "false");
211 if (isNumeric(val)) {
212 return Long.toString(((Number)val).longValue());
214 if (val instanceof Float) {
215 return Float.toString((Float)val);
217 if (val instanceof Double) {
218 return Double.toString((Double)val);
223 public static Number numberValue(Object val) {
224 if (val instanceof String) {
226 return Double.valueOf((String)val);
227 } catch (NumberFormatException ex) {
231 if (val instanceof Boolean) {
232 return (Boolean)val ? 1 : 0;
237 public static Character charValue(Object val) {
238 if (val instanceof Number) {
239 return Character.toChars(numberValue(val).intValue())[0];
241 if (val instanceof Boolean) {
242 return (Boolean)val ? (char)1 : (char)0;
244 if (val instanceof String) {
245 String s = (String)val;
246 return s.isEmpty() ? (char)0 : s.charAt(0);
248 return (Character)val;
251 public static Boolean boolValue(Object val) {
252 if (val instanceof String) {
253 return Boolean.parseBoolean((String)val);
255 if (val instanceof Number) {
256 return numberValue(val).doubleValue() != 0.0;
259 return Boolean.TRUE.equals(val);
262 public static Object find(Object object, Bindings model) {
263 if (object == null) {
266 if (object instanceof JSONList) {
267 return ((JSONList<?>) object).koData();
269 if (object instanceof Collection) {
270 return JSONList.koData((Collection<?>) object, model);
272 Proto proto = findProto(object);
276 final Bindings b = PropertyBindingAccessor.getBindings(proto, true);
277 return b == null ? null : b.koData();
280 private static Proto findProto(Object object) {
281 Proto.Type<?> type = JSON.findType(object.getClass());
285 final Proto proto = PropertyBindingAccessor.protoFor(type, object);
289 public static Object find(Object object) {
290 return find(object, null);
293 public static void applyBindings(Object object) {
294 final Proto proto = findProto(object);
296 throw new IllegalArgumentException("Not a model: " + object.getClass());
298 proto.applyBindings();
301 public static void loadJSON(
302 BrwsrCtx c, RcvrJSON callback,
303 String urlBefore, String urlAfter, String method,
306 JSONCall call = PropertyBindingAccessor.createCall(c, callback, urlBefore, urlAfter, method, data);
307 Transfer t = findTransfer(c);
310 public static WS openWS(
311 BrwsrCtx c, RcvrJSON r, String url, Object data
313 WS ws = WSImpl.create(findWSTransfer(c), r);
314 ws.send(c, url, data);
318 public static abstract class WS {
322 public abstract void send(BrwsrCtx ctx, String url, Object model);
325 private static final class WSImpl<Socket> extends WS {
327 private final WSTransfer<Socket> trans;
328 private final RcvrJSON rcvr;
329 private Socket socket;
330 private String prevURL;
332 private WSImpl(WSTransfer<Socket> trans, RcvrJSON rcvr) {
337 static <Socket> WS create(WSTransfer<Socket> t, RcvrJSON r) {
338 return new WSImpl<Socket>(t, r);
342 public void send(BrwsrCtx ctx, String url, Object data) {
346 throw new IllegalStateException("WebSocket is not opened yet. Call with null data, was: " + data);
348 JSONCall call = PropertyBindingAccessor.createCall(ctx, rcvr, url, null, "WebSocket", null);
349 socket = trans.open(url, call);
358 if (!prevURL.equals(url)) {
359 throw new IllegalStateException(
360 "Can't call to different URL " + url + " was: " + prevURL + "!"
361 + " Close the socket by calling it will null data first!"
364 JSONCall call = PropertyBindingAccessor.createCall(ctx, rcvr, prevURL, null, "WebSocket", data);
370 private static final Map<Class,Proto.Type<?>> modelTypes;
372 modelTypes = new HashMap<Class, Proto.Type<?>>();
374 public static void register(Class c, Proto.Type<?> type) {
375 modelTypes.put(c, type);
378 public static boolean isModel(Class<?> clazz) {
379 return findType(clazz) != null;
382 static Proto.Type<?> findType(Class<?> clazz) {
383 for (int i = 0; i < 2; i++) {
384 Proto.Type<?> from = modelTypes.get(clazz);
394 public static <Model> Model bindTo(Model model, BrwsrCtx c) {
395 Proto.Type<Model> from = (Proto.Type<Model>) findType(model.getClass());
397 throw new IllegalArgumentException();
399 return PropertyBindingAccessor.clone(from, model, c);
402 public static <T> T readStream(BrwsrCtx c, Class<T> modelClazz, InputStream data)
404 Transfer tr = findTransfer(c);
405 return read(c, modelClazz, tr.toJSON((InputStream)data));
407 public static <T> T read(BrwsrCtx c, Class<T> modelClazz, Object data) {
411 if (modelClazz == String.class) {
412 return modelClazz.cast(data.toString());
414 for (int i = 0; i < 2; i++) {
415 Proto.Type<?> from = modelTypes.get(modelClazz);
417 initClass(modelClazz);
419 return modelClazz.cast(PropertyBindingAccessor.readFrom(from, c, data));
422 throw new NullPointerException();
424 static void initClass(Class<?> modelClazz) {
426 // try to resolve the class
429 l = modelClazz.getClassLoader();
430 } catch (SecurityException ex) {
434 Class.forName(modelClazz.getName(), true, l);
436 modelClazz.newInstance();
437 } catch (Exception ex) {
438 // ignore and try again
442 private static final class EmptyTech
443 implements Technology<Object>, Transfer, WSTransfer<Void> {
444 private static final EmptyTech EMPTY = new EmptyTech();
447 public Object wrapModel(Object model) {
452 public void valueHasMutated(Object data, String propertyName) {
456 public void bind(PropertyBinding b, Object model, Object data) {
460 public void expose(FunctionBinding fb, Object model, Object d) {
464 public void applyBindings(Object data) {
468 public Object wrapArray(Object[] arr) {
473 public void extract(Object obj, String[] props, Object[] values) {
474 for (int i = 0; i < values.length; i++) {
480 public void loadJSON(JSONCall call) {
481 call.notifyError(new UnsupportedOperationException());
485 public <M> M toModel(Class<M> modelClass, Object data) {
486 return modelClass.cast(data);
490 public Object toJSON(InputStream is) throws IOException {
491 throw new IOException("Not supported");
495 public synchronized void runSafe(Runnable r) {
500 public Void open(String url, JSONCall onReply) {
501 onReply.notifyError(new UnsupportedOperationException("WebSockets not supported!"));
506 public void send(Void socket, JSONCall data) {
510 public void close(Void socket) {