All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.netbeans.html.json.impl.JSON Maven / Gradle / Ivy

Go to download

API for smooth representation of JSON objects in Java. Write your application in Java and present it using modern HTML rendering technologies like Knockout.

The newest version!
/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.netbeans.html.json.impl;

import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import net.java.html.BrwsrCtx;
import org.netbeans.html.context.spi.Contexts;
import org.netbeans.html.json.spi.FunctionBinding;
import org.netbeans.html.json.spi.JSONCall;
import org.netbeans.html.json.spi.PropertyBinding;
import org.netbeans.html.json.spi.Proto;
import org.netbeans.html.json.spi.Technology;
import org.netbeans.html.json.spi.Transfer;
import org.netbeans.html.json.spi.WSTransfer;

/**
 *
 * @author Jaroslav Tulach
 */
public final class JSON {
    private JSON() {
    }

    static Technology findTechnology(BrwsrCtx c) {
        Technology t = Contexts.find(c, Technology.class);
        return t == null ? EmptyTech.EMPTY : t;
    }

    public static Transfer findTransfer(BrwsrCtx c) {
        Transfer t = Contexts.find(c, Transfer.class);
        return t == null ? EmptyTech.EMPTY : t;
    }

    public static WSTransfer findWSTransfer(BrwsrCtx c) {
        WSTransfer t = Contexts.find(c, WSTransfer.class);
        return t == null ? EmptyTech.EMPTY : t;
    }

    public static  void readBindings(BrwsrCtx c, M model, Object value) {
        Technology tech = findTechnology(c);
        if (tech instanceof Technology.BatchCopy) {
            Proto p = findProto(model);
            PropertyBindingAccessor.getBindings(p, true, value);
        }
    }
    public static void extract(BrwsrCtx c, Object value, String[] props, Object[] values) {
        Transfer t = findTransfer(c);
        t.extract(value, props, values);
    }

    private static Object getProperty(BrwsrCtx c, Object obj, String prop) {
        if (prop == null) return obj;

        String[] arr = { prop };
        Object[] val = { null };
        extract(c, obj, arr, val);
        return val[0];
    }

    public static String toJSON(Object value) {
        if (value == null) {
            return "null";
        }
        if (value instanceof Enum) {
            value = value.toString();
        }
        if (value instanceof Character) {
            value = Character.toString((Character)value);
        }
        if (value instanceof String) {
            String s = (String)value;
            int len = s.length();
            StringBuilder sb = new StringBuilder(len + 10);
            sb.append('"');
            for (int i = 0; i < len; i++) {
                char ch = s.charAt(i);
                switch (ch) {
                    case '\"': sb.append("\\\""); break;
                    case '\n': sb.append("\\n"); break;
                    case '\r': sb.append("\\r"); break;
                    case '\t': sb.append("\\t"); break;
                    case '\\': sb.append("\\\\"); break;
                    default: sb.append(ch);
                }
            }
            sb.append('"');
            return sb.toString();
        }
        return value.toString();
    }

    public static String toString(BrwsrCtx c, Object obj, String prop) {
        obj = getProperty(c, obj, prop);
        return obj == null ? null : obj.toString();
    }
    public static Number toNumber(BrwsrCtx c, Object obj, String prop) {
        obj = getProperty(c, obj, prop);
        if (obj instanceof Character) {
            obj = (int)(Character)obj;
        }
        if (!(obj instanceof Number)) {
            obj = Double.NaN;
        }
        return (Number)obj;
    }
    public static  M toModel(BrwsrCtx c, Class aClass, Object data, Object object) {
        Technology t = findTechnology(c);
        Object o = t.toModel(aClass, data);
        return aClass.cast(o);
    }

    public static boolean isSame(int a, int b) {
        return a == b;
    }

    public static boolean isSame(double a, double b) {
        return a == b;
    }

    public static boolean isSame(Object a, Object b) {
        if (a == b) {
            return true;
        }
        if (a == null || b == null) {
            return false;
        }
        return a.equals(b);
    }

    public static int hashPlus(Object o, int h) {
        return o == null ? h : h ^ o.hashCode();
    }

    public static  T extractValue(Class type, Object val) {
        if (Number.class.isAssignableFrom(type)) {
            val = numberValue(val);
        }
        if (Boolean.class == type) {
            val = boolValue(val);
        }
        if (String.class == type) {
            val = stringValue(val);
        }
        if (Character.class == type) {
            val = charValue(val);
        }
        if (Integer.class == type) {
            val = val instanceof Number ? ((Number)val).intValue() : 0;
        }
        if (Long.class == type) {
            val = val instanceof Number  ? ((Number)val).longValue() : 0;
        }
        if (Short.class == type) {
            val = val instanceof Number ? ((Number)val).shortValue() : 0;
        }
        if (Byte.class == type) {
            val = val instanceof Number ? ((Number)val).byteValue() : 0;
        }
        if (Double.class == type) {
            val = val instanceof Number ? ((Number)val).doubleValue() : Double.NaN;
        }
        if (Float.class == type) {
            val = val instanceof Number ? ((Number)val).floatValue() : Float.NaN;
        }
        return type.cast(val);
    }

    static boolean isNumeric(Object val) {
        return ((val instanceof Integer) || (val instanceof Long) || (val instanceof Short) || (val instanceof Byte));
    }

    public static String stringValue(Object val) {
        if (val instanceof Boolean) {
            return ((Boolean)val ? "true" : "false");
        }
        if (isNumeric(val)) {
            return Long.toString(((Number)val).longValue());
        }
        if (val instanceof Float) {
            return Float.toString((Float)val);
        }
        if (val instanceof Double) {
            return Double.toString((Double)val);
        }
        return (String)val;
    }

    public static Number numberValue(Object val) {
        if (val instanceof String) {
            try {
                return Double.valueOf((String)val);
            } catch (NumberFormatException ex) {
                return Double.NaN;
            }
        }
        if (val instanceof Boolean) {
            return (Boolean)val ? 1 : 0;
        }
        return (Number)val;
    }

    public static Character charValue(Object val) {
        if (val instanceof Number) {
            return Character.toChars(numberValue(val).intValue())[0];
        }
        if (val instanceof Boolean) {
            return (Boolean)val ? (char)1 : (char)0;
        }
        if (val instanceof String) {
            String s = (String)val;
            return s.isEmpty() ? (char)0 : s.charAt(0);
        }
        return (Character)val;
    }

    public static Boolean boolValue(Object val) {
        if (val instanceof String) {
            return Boolean.parseBoolean((String)val);
        }
        if (val instanceof Number) {
            return numberValue(val).doubleValue() != 0.0;
        }

        return Boolean.TRUE.equals(val);
    }

    public static Object find(Object object, Bindings model) {
        if (object == null) {
            return null;
        }
        if (object instanceof JSONList) {
            return ((JSONList) object).koData();
        }
        if (object instanceof Collection) {
            return JSONList.koData((Collection) object, model);
        }
        if (
            object instanceof String ||
            object instanceof Boolean ||
            object instanceof Number ||
            object instanceof Character ||
            object instanceof Enum
        ) {
            return object;
        }
        Proto proto = findProto(object);
        if (proto == null) {
            return null;
        }
        final Bindings b = PropertyBindingAccessor.getBindings(proto, true, null);
        return b == null ? null : b.jsObj();
    }

    private static Proto findProto(Object object) {
        Proto.Type type = JSON.findType(object.getClass());
        if (type == null) {
            return null;
        }
        final Proto proto = PropertyBindingAccessor.protoFor(type, object);
        return proto;
    }

    public static Object find(Object object) {
        return find(object, null);
    }

    public static void applyBindings(Object object, String id) {
        final Proto proto = findProto(object);
        if (proto == null) {
            throw new IllegalArgumentException("Not a model: " + object.getClass());
        }
        proto.applyBindings(id);
    }

    public static abstract class WS {
        private WS() {
        }

        public abstract void send(BrwsrCtx ctx, String headers, String url, Object model);
        public static  WS create(WSTransfer t, RcvrJSON r) {
            return new WSImpl(t, r);
        }
    }

    private static final class WSImpl extends WS {

        private final WSTransfer trans;
        private final RcvrJSON rcvr;
        private Socket socket;
        private String prevURL;

        private WSImpl(WSTransfer trans, RcvrJSON rcvr) {
            this.trans = trans;
            this.rcvr = rcvr;
        }

        @Override
        public void send(BrwsrCtx ctx, String headers, String url, Object data) {
            Socket s = socket;
            if (s == null) {
                if (data != null) {
                    throw new IllegalStateException("WebSocket is not opened yet. Call with null data, was: " + data);
                }
                JSONCall call = PropertyBindingAccessor.createCall(ctx, rcvr, headers, url, null, "WebSocket", null);
                socket = trans.open(url, call);
                prevURL = url;
                return;
            }
            if (data == null) {
                trans.close(s);
                socket = null;
                return;
            }
            if (!prevURL.equals(url)) {
                throw new IllegalStateException(
                    "Can't call to different URL " + url + " was: " + prevURL + "!"
                    + " Close the socket by calling it will null data first!"
                );
            }
            JSONCall call = PropertyBindingAccessor.createCall(ctx, rcvr, headers, prevURL, null, "WebSocket", data);
            trans.send(s, call);
        }

    }


    static ModelTypes initModelTypes(String preload, String implName) {
        ModelTypes types = null;
        try {
            Class.forName(preload);
            Class clazz = Class.forName(implName);
            types = (ModelTypes) clazz.newInstance();
        } catch (ClassNotFoundException ex) {
            // OK, not supported
        } catch (NoClassDefFoundError ex) {
            // OK, not supported
        } catch (Throwable ex) {
            ex.printStackTrace();
        } finally {
            if (types == null) {
                types = new LinkedListTypes();
            }
        }
        return types;
    }

    public static void register(Class c, Proto.Type type) {
        ModelTypes.MODELS.find(c)[0] = type;
    }

    public static boolean isModel(Class clazz) {
        return findType(clazz) != null;
    }

    static Proto.Type findType(Class clazz) {
        for (int i = 0; i < 2; i++) {
            Proto.Type from = ModelTypes.MODELS.find(clazz)[0];
            if (from == null) {
                initClass(clazz);
            } else {
                return from;
            }
        }
        return null;
    }

    public static  Model bindTo(Model model, BrwsrCtx c) {
        Proto.Type from = (Proto.Type) findType(model.getClass());
        if (from == null) {
            throw new IllegalArgumentException();
        }
        return PropertyBindingAccessor.clone(from, model, c);
    }

    public static  T readStream(BrwsrCtx c, Class modelClazz, InputStream data, Collection collectTo)
    throws IOException {
        Transfer tr = findTransfer(c);
        Object rawJSON = tr.toJSON((InputStream)data);
        if (rawJSON instanceof Object[]) {
            final Object[] arr = (Object[])rawJSON;
            if (collectTo != null) {
                for (int i = 0; i < arr.length; i++) {
                    collectTo.add(read(c, modelClazz, arr[i]));
                }
                return null;
            }
            if (arr.length == 0) {
                throw new EOFException("Recieved an empty array");
            }
            rawJSON = arr[0];
        }
        T res = read(c, modelClazz, rawJSON);
        if (collectTo != null) {
            collectTo.add(res);
        }
        return res;
    }
    public static  T read(BrwsrCtx c, Class modelClazz, Object data) {
        if (data == null) {
            return null;
        }
        if (modelClazz == String.class) {
            return modelClazz.cast(data.toString());
        }
        for (int i = 0; i < 2; i++) {
            Proto.Type from = ModelTypes.MODELS.find(modelClazz)[0];
            if (from == null) {
                initClass(modelClazz);
            } else {
                return modelClazz.cast(PropertyBindingAccessor.readFrom(from, c, data));
            }
        }
        throw new NullPointerException();
    }
    static void initClass(Class modelClazz) {
        try {
            // try to resolve the class
            ClassLoader l;
            try {
                l = modelClazz.getClassLoader();
            } catch (SecurityException ex) {
                l = null;
            }
            if (l != null) {
                Class.forName(modelClazz.getName(), true, l);
            }
            modelClazz.newInstance();
        } catch (Exception ex) {
            // ignore and try again
        }
    }

    private static final class EmptyTech
    implements Technology, Transfer, WSTransfer {
        private static final EmptyTech EMPTY = new EmptyTech();

        @Override
        public Object wrapModel(Object model) {
            return model;
        }

        @Override
        public void valueHasMutated(Object data, String propertyName) {
        }

        @Override
        public void bind(PropertyBinding b, Object model, Object data) {
        }

        @Override
        public void expose(FunctionBinding fb, Object model, Object d) {
        }

        @Override
        public void applyBindings(Object data) {
        }

        @Override
        public Object wrapArray(Object[] arr) {
            return arr;
        }

        @Override
        public void extract(Object obj, String[] props, Object[] values) {
            for (int i = 0; i < values.length; i++) {
                values[i] = null;
            }
        }

        @Override
        public void loadJSON(JSONCall call) {
            call.notifyError(new UnsupportedOperationException());
        }

        @Override
        public  M toModel(Class modelClass, Object data) {
            return modelClass.cast(data);
        }

        @Override
        public Object toJSON(InputStream is) throws IOException {
            throw new IOException("Not supported");
        }

        @Override
        public void runSafe(Runnable r) {
            r.run();
        }

        @Override
        public Void open(String url, JSONCall onReply) {
            onReply.notifyError(new UnsupportedOperationException("WebSockets not supported!"));
            return null;
        }

        @Override
        public void send(Void socket, JSONCall data) {
        }

        @Override
        public void close(Void socket) {
        }
    }

}