json/src/main/java/org/netbeans/html/json/impl/JSON.java
branchnetbeans
changeset 362 92fb71afdc0e
parent 358 80702021b851
child 365 5c93ad8c7a15
     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 +}