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.EOFException;
46 import java.io.IOException;
47 import java.io.InputStream;
48 import java.util.Collection;
49 import java.util.HashMap;
51 import net.java.html.BrwsrCtx;
52 import org.apidesign.html.context.spi.Contexts;
53 import org.apidesign.html.json.spi.FunctionBinding;
54 import org.apidesign.html.json.spi.JSONCall;
55 import org.apidesign.html.json.spi.PropertyBinding;
56 import org.apidesign.html.json.spi.Proto;
57 import org.apidesign.html.json.spi.Technology;
58 import org.apidesign.html.json.spi.Transfer;
59 import org.apidesign.html.json.spi.WSTransfer;
63 * @author Jaroslav Tulach
65 public final class JSON {
69 static Technology<?> findTechnology(BrwsrCtx c) {
70 Technology<?> t = Contexts.find(c, Technology.class);
71 return t == null ? EmptyTech.EMPTY : t;
74 static Transfer findTransfer(BrwsrCtx c) {
75 Transfer t = Contexts.find(c, Transfer.class);
76 return t == null ? EmptyTech.EMPTY : t;
79 static WSTransfer<?> findWSTransfer(BrwsrCtx c) {
80 WSTransfer<?> t = Contexts.find(c, WSTransfer.class);
81 return t == null ? EmptyTech.EMPTY : t;
84 public static void extract(BrwsrCtx c, Object value, String[] props, Object[] values) {
85 Transfer t = findTransfer(c);
86 t.extract(value, props, values);
89 private static Object getProperty(BrwsrCtx c, Object obj, String prop) {
90 if (prop == null) return obj;
92 String[] arr = { prop };
93 Object[] val = { null };
94 extract(c, obj, arr, val);
98 public static String toJSON(Object value) {
102 if (value instanceof Enum) {
103 value = value.toString();
105 if (value instanceof String) {
106 String s = (String)value;
107 int len = s.length();
108 StringBuilder sb = new StringBuilder(len + 10);
110 for (int i = 0; i < len; i++) {
111 char ch = s.charAt(i);
113 case '\"': sb.append("\\\""); break;
114 case '\n': sb.append("\\n"); break;
115 case '\r': sb.append("\\r"); break;
116 case '\t': sb.append("\\t"); break;
117 case '\\': sb.append("\\\\"); break;
118 default: sb.append(ch);
122 return sb.toString();
124 return value.toString();
127 public static String toString(BrwsrCtx c, Object obj, String prop) {
128 obj = getProperty(c, obj, prop);
129 return obj instanceof String ? (String)obj : null;
131 public static Number toNumber(BrwsrCtx c, Object obj, String prop) {
132 obj = getProperty(c, obj, prop);
133 if (!(obj instanceof Number)) {
138 public static <M> M toModel(BrwsrCtx c, Class<M> aClass, Object data, Object object) {
139 Technology<?> t = findTechnology(c);
140 Object o = t.toModel(aClass, data);
141 return aClass.cast(o);
144 public static boolean isSame(int a, int b) {
148 public static boolean isSame(double a, double b) {
152 public static boolean isSame(Object a, Object b) {
156 if (a == null || b == null) {
162 public static int hashPlus(Object o, int h) {
163 return o == null ? h : h ^ o.hashCode();
166 public static <T> T extractValue(Class<T> type, Object val) {
167 if (Number.class.isAssignableFrom(type)) {
168 val = numberValue(val);
170 if (Boolean.class == type) {
171 val = boolValue(val);
173 if (String.class == type) {
174 val = stringValue(val);
176 if (Character.class == type) {
177 val = charValue(val);
179 if (Integer.class == type) {
180 val = val instanceof Number ? ((Number)val).intValue() : 0;
182 if (Long.class == type) {
183 val = val instanceof Number ? ((Number)val).longValue() : 0;
185 if (Short.class == type) {
186 val = val instanceof Number ? ((Number)val).shortValue() : 0;
188 if (Byte.class == type) {
189 val = val instanceof Number ? ((Number)val).byteValue() : 0;
191 if (Double.class == type) {
192 val = val instanceof Number ? ((Number)val).doubleValue() : Double.NaN;
194 if (Float.class == type) {
195 val = val instanceof Number ? ((Number)val).floatValue() : Float.NaN;
197 return type.cast(val);
200 static boolean isNumeric(Object val) {
201 return ((val instanceof Integer) || (val instanceof Long) || (val instanceof Short) || (val instanceof Byte));
204 public static String stringValue(Object val) {
205 if (val instanceof Boolean) {
206 return ((Boolean)val ? "true" : "false");
208 if (isNumeric(val)) {
209 return Long.toString(((Number)val).longValue());
211 if (val instanceof Float) {
212 return Float.toString((Float)val);
214 if (val instanceof Double) {
215 return Double.toString((Double)val);
220 public static Number numberValue(Object val) {
221 if (val instanceof String) {
223 return Double.valueOf((String)val);
224 } catch (NumberFormatException ex) {
228 if (val instanceof Boolean) {
229 return (Boolean)val ? 1 : 0;
234 public static Character charValue(Object val) {
235 if (val instanceof Number) {
236 return Character.toChars(numberValue(val).intValue())[0];
238 if (val instanceof Boolean) {
239 return (Boolean)val ? (char)1 : (char)0;
241 if (val instanceof String) {
242 String s = (String)val;
243 return s.isEmpty() ? (char)0 : s.charAt(0);
245 return (Character)val;
248 public static Boolean boolValue(Object val) {
249 if (val instanceof String) {
250 return Boolean.parseBoolean((String)val);
252 if (val instanceof Number) {
253 return numberValue(val).doubleValue() != 0.0;
256 return Boolean.TRUE.equals(val);
259 public static Object find(Object object, Bindings model) {
260 if (object == null) {
263 if (object instanceof JSONList) {
264 return ((JSONList<?>) object).koData();
266 if (object instanceof Collection) {
267 return JSONList.koData((Collection<?>) object, model);
270 object instanceof String ||
271 object instanceof Boolean ||
272 object instanceof Number ||
273 object instanceof Character ||
274 object instanceof Enum<?>
278 Proto proto = findProto(object);
282 final Bindings b = PropertyBindingAccessor.getBindings(proto, true);
283 return b == null ? null : b.koData();
286 private static Proto findProto(Object object) {
287 Proto.Type<?> type = JSON.findType(object.getClass());
291 final Proto proto = PropertyBindingAccessor.protoFor(type, object);
295 public static Object find(Object object) {
296 return find(object, null);
299 public static void applyBindings(Object object) {
300 final Proto proto = findProto(object);
302 throw new IllegalArgumentException("Not a model: " + object.getClass());
304 proto.applyBindings();
307 public static void loadJSON(
308 BrwsrCtx c, RcvrJSON callback,
309 String urlBefore, String urlAfter, String method,
312 JSONCall call = PropertyBindingAccessor.createCall(c, callback, urlBefore, urlAfter, method, data);
313 Transfer t = findTransfer(c);
316 public static WS openWS(
317 BrwsrCtx c, RcvrJSON r, String url, Object data
319 WS ws = WSImpl.create(findWSTransfer(c), r);
320 ws.send(c, url, data);
324 public static abstract class WS {
328 public abstract void send(BrwsrCtx ctx, String url, Object model);
331 private static final class WSImpl<Socket> extends WS {
333 private final WSTransfer<Socket> trans;
334 private final RcvrJSON rcvr;
335 private Socket socket;
336 private String prevURL;
338 private WSImpl(WSTransfer<Socket> trans, RcvrJSON rcvr) {
343 static <Socket> WS create(WSTransfer<Socket> t, RcvrJSON r) {
344 return new WSImpl<Socket>(t, r);
348 public void send(BrwsrCtx ctx, String url, Object data) {
352 throw new IllegalStateException("WebSocket is not opened yet. Call with null data, was: " + data);
354 JSONCall call = PropertyBindingAccessor.createCall(ctx, rcvr, url, null, "WebSocket", null);
355 socket = trans.open(url, call);
364 if (!prevURL.equals(url)) {
365 throw new IllegalStateException(
366 "Can't call to different URL " + url + " was: " + prevURL + "!"
367 + " Close the socket by calling it will null data first!"
370 JSONCall call = PropertyBindingAccessor.createCall(ctx, rcvr, prevURL, null, "WebSocket", data);
376 private static final Map<Class,Proto.Type<?>> modelTypes;
378 modelTypes = new HashMap<Class, Proto.Type<?>>();
380 public static void register(Class c, Proto.Type<?> type) {
381 modelTypes.put(c, type);
384 public static boolean isModel(Class<?> clazz) {
385 return findType(clazz) != null;
388 static Proto.Type<?> findType(Class<?> clazz) {
389 for (int i = 0; i < 2; i++) {
390 Proto.Type<?> from = modelTypes.get(clazz);
400 public static <Model> Model bindTo(Model model, BrwsrCtx c) {
401 Proto.Type<Model> from = (Proto.Type<Model>) findType(model.getClass());
403 throw new IllegalArgumentException();
405 return PropertyBindingAccessor.clone(from, model, c);
408 public static <T> T readStream(BrwsrCtx c, Class<T> modelClazz, InputStream data, Collection<? super T> collectTo)
410 Transfer tr = findTransfer(c);
411 Object rawJSON = tr.toJSON((InputStream)data);
412 if (rawJSON instanceof Object[]) {
413 final Object[] arr = (Object[])rawJSON;
414 if (collectTo != null) {
415 for (int i = 0; i < arr.length; i++) {
416 collectTo.add(read(c, modelClazz, arr[i]));
420 if (arr.length == 0) {
421 throw new EOFException("Recieved an empty array");
425 T res = read(c, modelClazz, rawJSON);
426 if (collectTo != null) {
431 public static <T> T read(BrwsrCtx c, Class<T> modelClazz, Object data) {
435 if (modelClazz == String.class) {
436 return modelClazz.cast(data.toString());
438 for (int i = 0; i < 2; i++) {
439 Proto.Type<?> from = modelTypes.get(modelClazz);
441 initClass(modelClazz);
443 return modelClazz.cast(PropertyBindingAccessor.readFrom(from, c, data));
446 throw new NullPointerException();
448 static void initClass(Class<?> modelClazz) {
450 // try to resolve the class
453 l = modelClazz.getClassLoader();
454 } catch (SecurityException ex) {
458 Class.forName(modelClazz.getName(), true, l);
460 modelClazz.newInstance();
461 } catch (Exception ex) {
462 // ignore and try again
466 private static final class EmptyTech
467 implements Technology<Object>, Transfer, WSTransfer<Void> {
468 private static final EmptyTech EMPTY = new EmptyTech();
471 public Object wrapModel(Object model) {
476 public void valueHasMutated(Object data, String propertyName) {
480 public void bind(PropertyBinding b, Object model, Object data) {
484 public void expose(FunctionBinding fb, Object model, Object d) {
488 public void applyBindings(Object data) {
492 public Object wrapArray(Object[] arr) {
497 public void extract(Object obj, String[] props, Object[] values) {
498 for (int i = 0; i < values.length; i++) {
504 public void loadJSON(JSONCall call) {
505 call.notifyError(new UnsupportedOperationException());
509 public <M> M toModel(Class<M> modelClass, Object data) {
510 return modelClass.cast(data);
514 public Object toJSON(InputStream is) throws IOException {
515 throw new IOException("Not supported");
519 public void runSafe(Runnable r) {
524 public Void open(String url, JSONCall onReply) {
525 onReply.notifyError(new UnsupportedOperationException("WebSockets not supported!"));
530 public void send(Void socket, JSONCall data) {
534 public void close(Void socket) {