During the API review process (bug 246133) the reviewers decided that in order to include html4j to NetBeans Platform, we need to stop using org.apidesign namespace and switch to NetBeans one. Repackaging all SPI packages into org.netbeans.html.smthng.spi.
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.netbeans.html.context.spi.Contexts;
53 import org.netbeans.html.json.spi.FunctionBinding;
54 import org.netbeans.html.json.spi.JSONCall;
55 import org.netbeans.html.json.spi.PropertyBinding;
56 import org.netbeans.html.json.spi.Proto;
57 import org.netbeans.html.json.spi.Technology;
58 import org.netbeans.html.json.spi.Transfer;
59 import org.netbeans.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) {