1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/JSON.java Mon Dec 16 16:59:43 2013 +0100
1.3 @@ -0,0 +1,474 @@
1.4 +/**
1.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
1.6 + *
1.7 + * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
1.8 + *
1.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
1.10 + * Other names may be trademarks of their respective owners.
1.11 + *
1.12 + * The contents of this file are subject to the terms of either the GNU
1.13 + * General Public License Version 2 only ("GPL") or the Common
1.14 + * Development and Distribution License("CDDL") (collectively, the
1.15 + * "License"). You may not use this file except in compliance with the
1.16 + * License. You can obtain a copy of the License at
1.17 + * http://www.netbeans.org/cddl-gplv2.html
1.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
1.19 + * specific language governing permissions and limitations under the
1.20 + * License. When distributing the software, include this License Header
1.21 + * Notice in each file and include the License file at
1.22 + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
1.23 + * particular file as subject to the "Classpath" exception as provided
1.24 + * by Oracle in the GPL Version 2 section of the License file that
1.25 + * accompanied this code. If applicable, add the following below the
1.26 + * License Header, with the fields enclosed by brackets [] replaced by
1.27 + * your own identifying information:
1.28 + * "Portions Copyrighted [year] [name of copyright owner]"
1.29 + *
1.30 + * Contributor(s):
1.31 + *
1.32 + * The Original Software is NetBeans. The Initial Developer of the Original
1.33 + * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
1.34 + *
1.35 + * If you wish your version of this file to be governed by only the CDDL
1.36 + * or only the GPL Version 2, indicate your decision by adding
1.37 + * "[Contributor] elects to include this software in this distribution
1.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
1.39 + * single choice of license, a recipient has the option to distribute
1.40 + * your version of this file under either the CDDL, the GPL Version 2 or
1.41 + * to extend the choice of license to its licensees as provided above.
1.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
1.43 + * Version 2 license, then the option applies only if the new code is
1.44 + * made subject to such option by the copyright holder.
1.45 + */
1.46 +package org.netbeans.html.json.impl;
1.47 +
1.48 +import java.io.IOException;
1.49 +import java.io.InputStream;
1.50 +import java.util.HashMap;
1.51 +import java.util.Map;
1.52 +import net.java.html.BrwsrCtx;
1.53 +import org.apidesign.html.context.spi.Contexts;
1.54 +import org.apidesign.html.json.spi.FunctionBinding;
1.55 +import org.apidesign.html.json.spi.JSONCall;
1.56 +import org.apidesign.html.json.spi.PropertyBinding;
1.57 +import org.apidesign.html.json.spi.Technology;
1.58 +import org.apidesign.html.json.spi.Transfer;
1.59 +import org.apidesign.html.json.spi.WSTransfer;
1.60 +
1.61 +/**
1.62 + *
1.63 + * @author Jaroslav Tulach <jtulach@netbeans.org>
1.64 + */
1.65 +public final class JSON {
1.66 + private JSON() {
1.67 + }
1.68 +
1.69 + static Technology<?> findTechnology(BrwsrCtx c) {
1.70 + Technology<?> t = Contexts.find(c, Technology.class);
1.71 + return t == null ? EmptyTech.EMPTY : t;
1.72 + }
1.73 +
1.74 + static Transfer findTransfer(BrwsrCtx c) {
1.75 + Transfer t = Contexts.find(c, Transfer.class);
1.76 + return t == null ? EmptyTech.EMPTY : t;
1.77 + }
1.78 +
1.79 + static WSTransfer<?> findWSTransfer(BrwsrCtx c) {
1.80 + WSTransfer<?> t = Contexts.find(c, WSTransfer.class);
1.81 + return t == null ? EmptyTech.EMPTY : t;
1.82 + }
1.83 +
1.84 + public static void runInBrowser(BrwsrCtx c, Runnable runnable) {
1.85 + findTechnology(c).runSafe(runnable);
1.86 + }
1.87 +
1.88 + public static void extract(BrwsrCtx c, Object value, String[] props, Object[] values) {
1.89 + Transfer t = findTransfer(c);
1.90 + t.extract(value, props, values);
1.91 + }
1.92 +
1.93 + private static Object getProperty(BrwsrCtx c, Object obj, String prop) {
1.94 + if (prop == null) return obj;
1.95 +
1.96 + String[] arr = { prop };
1.97 + Object[] val = { null };
1.98 + extract(c, obj, arr, val);
1.99 + return val[0];
1.100 + }
1.101 +
1.102 + public static Object toJSON(Object value) {
1.103 + if (value == null) {
1.104 + return "null";
1.105 + }
1.106 + if (value instanceof Enum) {
1.107 + value = value.toString();
1.108 + }
1.109 + if (value instanceof String) {
1.110 + String s = (String)value;
1.111 + int len = s.length();
1.112 + StringBuilder sb = new StringBuilder(len + 10);
1.113 + sb.append('"');
1.114 + for (int i = 0; i < len; i++) {
1.115 + char ch = s.charAt(i);
1.116 + switch (ch) {
1.117 + case '\"': sb.append("\\\""); break;
1.118 + case '\n': sb.append("\\n"); break;
1.119 + case '\r': sb.append("\\r"); break;
1.120 + case '\t': sb.append("\\t"); break;
1.121 + case '\\': sb.append("\\\\"); break;
1.122 + default: sb.append(ch);
1.123 + }
1.124 + }
1.125 + sb.append('"');
1.126 + return sb.toString();
1.127 + }
1.128 + return value.toString();
1.129 + }
1.130 +
1.131 + public static String toString(BrwsrCtx c, Object obj, String prop) {
1.132 + obj = getProperty(c, obj, prop);
1.133 + return obj instanceof String ? (String)obj : null;
1.134 + }
1.135 + public static Number toNumber(BrwsrCtx c, Object obj, String prop) {
1.136 + obj = getProperty(c, obj, prop);
1.137 + if (!(obj instanceof Number)) {
1.138 + obj = Double.NaN;
1.139 + }
1.140 + return (Number)obj;
1.141 + }
1.142 + public static <M> M toModel(BrwsrCtx c, Class<M> aClass, Object data, Object object) {
1.143 + Technology<?> t = findTechnology(c);
1.144 + Object o = t.toModel(aClass, data);
1.145 + return aClass.cast(o);
1.146 + }
1.147 +
1.148 + public static boolean isSame(int a, int b) {
1.149 + return a == b;
1.150 + }
1.151 +
1.152 + public static boolean isSame(double a, double b) {
1.153 + return a == b;
1.154 + }
1.155 +
1.156 + public static boolean isSame(Object a, Object b) {
1.157 + if (a == b) {
1.158 + return true;
1.159 + }
1.160 + if (a == null || b == null) {
1.161 + return false;
1.162 + }
1.163 + return a.equals(b);
1.164 + }
1.165 +
1.166 + public static int hashPlus(Object o, int h) {
1.167 + return o == null ? h : h ^ o.hashCode();
1.168 + }
1.169 +
1.170 + public static <T> T extractValue(Class<T> type, Object val) {
1.171 + if (Number.class.isAssignableFrom(type)) {
1.172 + val = numberValue(val);
1.173 + }
1.174 + if (Boolean.class == type) {
1.175 + val = boolValue(val);
1.176 + }
1.177 + if (String.class == type) {
1.178 + val = stringValue(val);
1.179 + }
1.180 + if (Character.class == type) {
1.181 + val = charValue(val);
1.182 + }
1.183 + if (Integer.class == type) {
1.184 + val = val instanceof Number ? ((Number)val).intValue() : 0;
1.185 + }
1.186 + if (Long.class == type) {
1.187 + val = val instanceof Number ? ((Number)val).longValue() : 0;
1.188 + }
1.189 + if (Short.class == type) {
1.190 + val = val instanceof Number ? ((Number)val).shortValue() : 0;
1.191 + }
1.192 + if (Byte.class == type) {
1.193 + val = val instanceof Number ? ((Number)val).byteValue() : 0;
1.194 + }
1.195 + if (Double.class == type) {
1.196 + val = val instanceof Number ? ((Number)val).doubleValue() : Double.NaN;
1.197 + }
1.198 + if (Float.class == type) {
1.199 + val = val instanceof Number ? ((Number)val).floatValue() : Float.NaN;
1.200 + }
1.201 + return type.cast(val);
1.202 + }
1.203 +
1.204 + protected static boolean isNumeric(Object val) {
1.205 + return ((val instanceof Integer) || (val instanceof Long) || (val instanceof Short) || (val instanceof Byte));
1.206 + }
1.207 +
1.208 + public static String stringValue(Object val) {
1.209 + if (val instanceof Boolean) {
1.210 + return ((Boolean)val ? "true" : "false");
1.211 + }
1.212 + if (isNumeric(val)) {
1.213 + return Long.toString(((Number)val).longValue());
1.214 + }
1.215 + if (val instanceof Float) {
1.216 + return Float.toString((Float)val);
1.217 + }
1.218 + if (val instanceof Double) {
1.219 + return Double.toString((Double)val);
1.220 + }
1.221 + return (String)val;
1.222 + }
1.223 +
1.224 + public static Number numberValue(Object val) {
1.225 + if (val instanceof String) {
1.226 + try {
1.227 + return Double.valueOf((String)val);
1.228 + } catch (NumberFormatException ex) {
1.229 + return Double.NaN;
1.230 + }
1.231 + }
1.232 + if (val instanceof Boolean) {
1.233 + return (Boolean)val ? 1 : 0;
1.234 + }
1.235 + return (Number)val;
1.236 + }
1.237 +
1.238 + public static Character charValue(Object val) {
1.239 + if (val instanceof Number) {
1.240 + return Character.toChars(numberValue(val).intValue())[0];
1.241 + }
1.242 + if (val instanceof Boolean) {
1.243 + return (Boolean)val ? (char)1 : (char)0;
1.244 + }
1.245 + if (val instanceof String) {
1.246 + String s = (String)val;
1.247 + return s.isEmpty() ? (char)0 : s.charAt(0);
1.248 + }
1.249 + return (Character)val;
1.250 + }
1.251 +
1.252 + public static Boolean boolValue(Object val) {
1.253 + if (val instanceof String) {
1.254 + return Boolean.parseBoolean((String)val);
1.255 + }
1.256 + if (val instanceof Number) {
1.257 + return numberValue(val).doubleValue() != 0.0;
1.258 + }
1.259 +
1.260 + return Boolean.TRUE.equals(val);
1.261 + }
1.262 +
1.263 + public static void loadJSON(
1.264 + BrwsrCtx c, RcvrJSON callback,
1.265 + String urlBefore, String urlAfter, String method,
1.266 + Object data
1.267 + ) {
1.268 + JSONCall call = PropertyBindingAccessor.createCall(c, callback, urlBefore, urlAfter, method, data);
1.269 + Transfer t = findTransfer(c);
1.270 + t.loadJSON(call);
1.271 + }
1.272 + public static WS openWS(
1.273 + BrwsrCtx c, RcvrJSON r, String url, Object data
1.274 + ) {
1.275 + WS ws = WSImpl.create(findWSTransfer(c), r);
1.276 + ws.send(c, url, data);
1.277 + return ws;
1.278 + }
1.279 +
1.280 + public static abstract class WS {
1.281 + private WS() {
1.282 + }
1.283 +
1.284 + public abstract void send(BrwsrCtx ctx, String url, Object model);
1.285 + }
1.286 +
1.287 + private static final class WSImpl<Socket> extends WS {
1.288 +
1.289 + private final WSTransfer<Socket> trans;
1.290 + private final RcvrJSON rcvr;
1.291 + private Socket socket;
1.292 + private String prevURL;
1.293 +
1.294 + private WSImpl(WSTransfer<Socket> trans, RcvrJSON rcvr) {
1.295 + this.trans = trans;
1.296 + this.rcvr = rcvr;
1.297 + }
1.298 +
1.299 + static <Socket> WS create(WSTransfer<Socket> t, RcvrJSON r) {
1.300 + return new WSImpl<Socket>(t, r);
1.301 + }
1.302 +
1.303 + @Override
1.304 + public void send(BrwsrCtx ctx, String url, Object data) {
1.305 + Socket s = socket;
1.306 + if (s == null) {
1.307 + if (data != null) {
1.308 + throw new IllegalStateException("WebSocket is not opened yet. Call with null data, was: " + data);
1.309 + }
1.310 + JSONCall call = PropertyBindingAccessor.createCall(ctx, rcvr, url, null, "WebSocket", null);
1.311 + socket = trans.open(url, call);
1.312 + prevURL = url;
1.313 + return;
1.314 + }
1.315 + if (data == null) {
1.316 + trans.close(s);
1.317 + socket = null;
1.318 + return;
1.319 + }
1.320 + if (!prevURL.equals(url)) {
1.321 + throw new IllegalStateException(
1.322 + "Can't call to different URL " + url + " was: " + prevURL + "!"
1.323 + + " Close the socket by calling it will null data first!"
1.324 + );
1.325 + }
1.326 + JSONCall call = PropertyBindingAccessor.createCall(ctx, rcvr, prevURL, null, "WebSocket", data);
1.327 + trans.send(s, call);
1.328 + }
1.329 +
1.330 + }
1.331 +
1.332 + private static final Map<Class,FromJSON<?>> froms;
1.333 + static {
1.334 + Map<Class,FromJSON<?>> m = new HashMap<Class,FromJSON<?>>();
1.335 + froms = m;
1.336 + }
1.337 + public static void register(FromJSON<?> from) {
1.338 + froms.put(from.factoryFor(), from);
1.339 + }
1.340 +
1.341 + public static boolean isModel(Class<?> clazz) {
1.342 + return findFrom(clazz) != null;
1.343 + }
1.344 +
1.345 + private static FromJSON<?> findFrom(Class<?> clazz) {
1.346 + for (int i = 0; i < 2; i++) {
1.347 + FromJSON<?> from = froms.get(clazz);
1.348 + if (from == null) {
1.349 + initClass(clazz);
1.350 + } else {
1.351 + return from;
1.352 + }
1.353 + }
1.354 + return null;
1.355 + }
1.356 +
1.357 + public static <Model> Model bindTo(Model model, BrwsrCtx c) {
1.358 + FromJSON<?> from = findFrom(model.getClass());
1.359 + if (from == null) {
1.360 + throw new IllegalArgumentException();
1.361 + }
1.362 + return (Model) from.cloneTo(model, c);
1.363 + }
1.364 +
1.365 + public static <T> T readStream(BrwsrCtx c, Class<T> modelClazz, InputStream data)
1.366 + throws IOException {
1.367 + Transfer tr = findTransfer(c);
1.368 + return read(c, modelClazz, tr.toJSON((InputStream)data));
1.369 + }
1.370 + public static <T> T read(BrwsrCtx c, Class<T> modelClazz, Object data) {
1.371 + if (data == null) {
1.372 + return null;
1.373 + }
1.374 + if (modelClazz == String.class) {
1.375 + return modelClazz.cast(data.toString());
1.376 + }
1.377 + for (int i = 0; i < 2; i++) {
1.378 + FromJSON<?> from = froms.get(modelClazz);
1.379 + if (from == null) {
1.380 + initClass(modelClazz);
1.381 + } else {
1.382 + return modelClazz.cast(from.read(c, data));
1.383 + }
1.384 + }
1.385 + throw new NullPointerException();
1.386 + }
1.387 + static void initClass(Class<?> modelClazz) {
1.388 + try {
1.389 + // try to resolve the class
1.390 + ClassLoader l;
1.391 + try {
1.392 + l = modelClazz.getClassLoader();
1.393 + } catch (SecurityException ex) {
1.394 + l = null;
1.395 + }
1.396 + if (l != null) {
1.397 + Class.forName(modelClazz.getName(), true, l);
1.398 + }
1.399 + modelClazz.newInstance();
1.400 + } catch (Exception ex) {
1.401 + // ignore and try again
1.402 + }
1.403 + }
1.404 +
1.405 + private static final class EmptyTech
1.406 + implements Technology<Object>, Transfer, WSTransfer<Void> {
1.407 + private static final EmptyTech EMPTY = new EmptyTech();
1.408 +
1.409 + @Override
1.410 + public Object wrapModel(Object model) {
1.411 + return model;
1.412 + }
1.413 +
1.414 + @Override
1.415 + public void valueHasMutated(Object data, String propertyName) {
1.416 + }
1.417 +
1.418 + @Override
1.419 + public void bind(PropertyBinding b, Object model, Object data) {
1.420 + }
1.421 +
1.422 + @Override
1.423 + public void expose(FunctionBinding fb, Object model, Object d) {
1.424 + }
1.425 +
1.426 + @Override
1.427 + public void applyBindings(Object data) {
1.428 + }
1.429 +
1.430 + @Override
1.431 + public Object wrapArray(Object[] arr) {
1.432 + return arr;
1.433 + }
1.434 +
1.435 + @Override
1.436 + public void extract(Object obj, String[] props, Object[] values) {
1.437 + for (int i = 0; i < values.length; i++) {
1.438 + values[i] = null;
1.439 + }
1.440 + }
1.441 +
1.442 + @Override
1.443 + public void loadJSON(JSONCall call) {
1.444 + call.notifyError(new UnsupportedOperationException());
1.445 + }
1.446 +
1.447 + @Override
1.448 + public <M> M toModel(Class<M> modelClass, Object data) {
1.449 + return modelClass.cast(data);
1.450 + }
1.451 +
1.452 + @Override
1.453 + public Object toJSON(InputStream is) throws IOException {
1.454 + throw new IOException("Not supported");
1.455 + }
1.456 +
1.457 + @Override
1.458 + public synchronized void runSafe(Runnable r) {
1.459 + r.run();
1.460 + }
1.461 +
1.462 + @Override
1.463 + public Void open(String url, JSONCall onReply) {
1.464 + onReply.notifyError(new UnsupportedOperationException("WebSockets not supported!"));
1.465 + return null;
1.466 + }
1.467 +
1.468 + @Override
1.469 + public void send(Void socket, JSONCall data) {
1.470 + }
1.471 +
1.472 + @Override
1.473 + public void close(Void socket) {
1.474 + }
1.475 + }
1.476 +
1.477 +}