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

cn.teleinfo.idpointer.sdk.core.GsonUtility Maven / Gradle / Ivy

Go to download

基于Java语言开发的工业互联网标识解析体系客户端软件开发工具包,应用通过集成 id-pointer-sdk,快速对接标识解析、标识注册、标识维护等功能服务。

The newest version!
/**********************************************************************\
 © COPYRIGHT 2019 Corporation for National Research Initiatives (CNRI);
                        All rights reserved.

        The HANDLE.NET software is made available subject to the
      Handle.Net Public License Agreement, which may be obtained at
          http://hdl.handle.net/20.1000/112 or hdl:20.1000/112
\**********************************************************************/

package cn.teleinfo.idpointer.sdk.core;

import cn.teleinfo.idpointer.sdk.core.stream.util.FastDateFormat;
import com.google.gson.*;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;

import java.lang.reflect.Type;
import java.math.BigInteger;
import java.net.InetAddress;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.*;
import java.security.spec.*;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class GsonUtility {
    /**
     * Register Handle.net type adapters on a given GsonBuilder, to enable serialization and deserialization of various Handle.net types.
     * @param gsonBuilder a GsonBuilder
     * @return the passed-in GsonBuilder.
     */
    public static GsonBuilder setup(GsonBuilder gsonBuilder) {
        gsonBuilder.registerTypeAdapter(HandleValue.class, new HandleValueGsonTypeAdapter());
        gsonBuilder.registerTypeAdapter(Transaction.class, new TransactionGsonTypeAdapter());
        gsonBuilder.registerTypeAdapter(ValueReference.class, new ValueReferenceGsonTypeAdapter());
        gsonBuilder.registerTypeAdapter(AdminRecord.class, new AdminRecordGsonTypeAdapter());
        gsonBuilder.registerTypeAdapter(SiteInfo.class, new SiteInfoGsonTypeAdapter());
        gsonBuilder.registerTypeAdapter(ServerInfo.class, new ServerInfoGsonTypeAdapter());
        gsonBuilder.registerTypeAdapter(Interface.class, new InterfaceGsonTypeAdapter());
        gsonBuilder.registerTypeHierarchyAdapter(AbstractResponse.class, new ResponseGsonTypeHierarchyAdapter());
        gsonBuilder.registerTypeHierarchyAdapter(PublicKey.class, new PublicKeyTypeHierarchyAdapter());
        gsonBuilder.registerTypeHierarchyAdapter(PrivateKey.class, new PrivateKeyTypeHierarchyAdapter());
        return gsonBuilder;
    }

    /**
     * Returns a GsonBuilder which can serialize and deserialize various Handle.net types.
     * @return a GsonBuilder which can serialize and deserialize various Handle.net types.
     */
    public static GsonBuilder getNewGsonBuilder() {
        return setup(new GsonBuilder());
    }

    /**
     * Returns a Gson instance which can serialize and deserialize various Handle.net types.  This Gson instance has HTML escaping disabled.
     * @return a Gson instance which can serialize and deserialize various Handle.net types.
     */
    public static Gson getGson() {
        return GsonHolder.gson;
    }

    /**
     * Returns a Gson instance which can serialize and deserialize various Handle.net types.  This Gson instance has HTML escaping disabled and pretty-printing enabled.
     * @return a Gson instance which can serialize and deserialize various Handle.net types.
     */
    public static Gson getPrettyGson() {
        return PrettyGsonHolder.prettyGson;
    }

    private static class GsonHolder {
        static Gson gson;
        static {
            gson = GsonUtility.setup(new GsonBuilder().disableHtmlEscaping()).create();
        }
    }

    private static class PrettyGsonHolder {
        static Gson prettyGson;
        static {
            prettyGson = GsonUtility.setup(new GsonBuilder().disableHtmlEscaping().setPrettyPrinting()).create();
        }
    }

    private static String lowerCaseFirst(String s) {
        return s.substring(0, 1).toLowerCase() + s.substring(1);
    }

    static JsonObject lowerCaseIfNeeded(JsonObject json) {
        if (json.has("ServerList") || json.has("Address") || json.has("Port")) {
            JsonObject obj = new JsonObject();
            for (Map.Entry entry : json.entrySet()) {
                obj.add(lowerCaseFirst(entry.getKey()), entry.getValue());
            }
            return obj;
        } else return json;
    }

    static String secondsTimestampAsString(int seconds) {
        return FastDateFormat.formatUtc(FastDateFormat.FormatSpec.ISO8601_NO_MS, 1000L * seconds);
    }

    static int secondsTimestampFromString(String s) throws ParseException {
        return (int) (FastDateFormat.parseUtc(s) / 1000);
    }

    static JsonElement serializeData(HandleValue value, JsonSerializationContext context) {
        try {
            if (value.hasType(Common.ADMIN_TYPE)) {
                AdminRecord adminRecord = new AdminRecord();
                Encoder.decodeAdminRecord(value.getData(), 0, adminRecord);
                return dataOfType("admin", context.serialize(adminRecord));
            } else if (value.hasType(Common.ADMIN_GROUP_TYPE)) {
                ValueReference[] refs = Encoder.decodeValueReferenceList(value.getData(), 0);
                return dataOfType("vlist", context.serialize(refs));
            } else if (value.hasType(Common.SITE_INFO_TYPE) || value.hasType(Common.DERIVED_PREFIX_SITE_TYPE) || value.hasType(Common.LEGACY_DERIVED_PREFIX_SITE_TYPE)) {
                SiteInfo site = new SiteInfo();
                Encoder.decodeSiteInfoRecord(value.getData(), 0, site);
                return dataOfType("site", context.serialize(site));
            } else if (value.hasType(Common.PUBLIC_KEY_TYPE)) {
                PublicKey key = Util.getPublicKeyFromBytes(value.getData());
                return dataOfType("key", context.serialize(key));
            }
        } catch (Exception e) {
            // fall-through
        }
        if (!Util.looksLikeBinary(value.getData())) {
            return serializeString(Util.decodeString(value.getData()));
        } else return serializeBinary(value.getData());
    }

    private static JsonElement dataOfType(String type, JsonElement value) {
        JsonObject obj = new JsonObject();
        obj.addProperty("format", type);
        obj.add("value", value);
        return obj;
    }

    static JsonElement serializeBinary(byte[] bytes) {
        JsonObject obj = new JsonObject();
        obj.addProperty("format", "base64");
        obj.addProperty("value", Base64.encodeBase64String(bytes));
        return obj;
    }

    static JsonElement serializeString(String s) {
        JsonObject obj = new JsonObject();
        obj.addProperty("format", "string");
        obj.addProperty("value", s);
        return obj;
    }

    static byte[] deserializeData(JsonElement json, JsonDeserializationContext context) throws DecoderException, ParseException {
        if (json.isJsonPrimitive()) {
            return Util.encodeString(json.getAsString());
        } else {
            JsonObject obj = json.getAsJsonObject();
            String format = obj.get("format").getAsString();
            JsonElement value = obj.get("value");
            if (format.equalsIgnoreCase("string")) {
                return Util.encodeString(value.getAsString());
            } else if (format.equalsIgnoreCase("base64")) {
                return Base64.decodeBase64(value.getAsString());
            } else if (format.equalsIgnoreCase("hex")) {
                return Hex.decodeHex(value.getAsString().toCharArray());
            } else if (format.equalsIgnoreCase("admin")) {
                AdminRecord adminRecord = context.deserialize(value, AdminRecord.class);
                return Encoder.encodeAdminRecord(adminRecord);
            } else if (format.equalsIgnoreCase("vlist")) {
                ValueReference[] refs = context.deserialize(value, ValueReference[].class);
                return Encoder.encodeValueReferenceList(refs);
            } else if (format.equalsIgnoreCase("site")) {
                SiteInfo site = context.deserialize(value, SiteInfo.class);
                return Encoder.encodeSiteInfoRecord(site);
            } else if (format.equalsIgnoreCase("key")) {
                PublicKey key = context.deserialize(value, PublicKey.class);
                try {
                    return Util.getBytesFromPublicKey(key);
                } catch (HandleException e) {
                    ParseException pe = new ParseException("Unable to deserialize public key", 0);
                    pe.initCause(e);
                    throw pe;
                }
            } else throw new ParseException("Unexpected type " + format, 0);
        }
    }

    /**
     * Serialize a response, adding in the handle value from the given request.
     *
     * @param req a request
     * @param resp a response
     * @return The response, serialized as a JSON tree, with the "handle" value from the request if not already in the response.
     */
    public static JsonElement serializeResponseToRequest(AbstractRequest req, AbstractResponse resp) {
        JsonObject json = getGson().toJsonTree(resp).getAsJsonObject();
        if (json.has("handle")) return json;
        if (req != null && req.handle != null && req.handle.length > 0 && !Util.equals(Common.BLANK_HANDLE, req.handle)) json.addProperty("handle", Util.decodeString(req.handle));
        return json;
    }

    public static class HandleValueGsonTypeAdapter implements JsonSerializer, JsonDeserializer {
        @Override
        public JsonElement serialize(HandleValue value, Type type, JsonSerializationContext context) {
            JsonObject json = new JsonObject();
            json.addProperty("index", Integer.valueOf(value.index));
            json.addProperty("type", Util.decodeString(value.type));
            json.add("data", serializeData(value, context));
            if (unusualPermissions(value)) json.add("permissions", serializePermissions(value));
            json.add("ttl", serializeTtl(value));
            if (value.timestamp != 0) {
                json.addProperty("timestamp", secondsTimestampAsString(value.timestamp));
            }
            if (value.references != null && value.references.length > 0) {
                json.add("references", context.serialize(value.references));
            }
            return json;
        }

        @Override
        public HandleValue deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
            try {
                JsonObject obj = json.getAsJsonObject();
                HandleValue value = new HandleValue();
                value.index = obj.get("index").getAsInt();
                value.type = Util.encodeString(obj.get("type").getAsString());
                value.data = deserializeData(obj.get("data"), context);
                deserializePermissions(value, obj);
                deserializeTtl(value, obj);
                if (obj.has("timestamp")) value.timestamp = secondsTimestampFromString(obj.get("timestamp").getAsString());
                JsonElement refs = obj.get("references");
                if (refs != null && !refs.isJsonNull()) {
                    value.references = context.deserialize(refs, ValueReference[].class);
                    ensureNoTrailingComma(value.references);
                }
                return value;
            } catch (JsonParseException e) {
                throw e;
            } catch (Exception e) {
                throw new JsonParseException(e);
            }
        }

        private static boolean unusualPermissions(HandleValue value) {
            return !value.adminRead || !value.adminWrite || !value.publicRead || value.publicWrite;
        }

        private static JsonElement serializeTtl(HandleValue value) {
            if (value.ttlType == HandleValue.TTL_TYPE_RELATIVE) {
                return new JsonPrimitive(Integer.valueOf(value.ttl));
            } else {
                return new JsonPrimitive(secondsTimestampAsString(value.ttl));
            }
        }

        private static void deserializeTtl(HandleValue value, JsonObject obj) throws ParseException {
            JsonElement ttl = obj.get("ttl");
            if (ttl != null) {
                JsonPrimitive ttlPrim = ttl.getAsJsonPrimitive();
                if (ttlPrim.isString()) {
                    value.ttlType = HandleValue.TTL_TYPE_ABSOLUTE;
                    value.ttl = secondsTimestampFromString(ttlPrim.getAsString());
                } else if (ttlPrim.isNumber()) {
                    value.ttlType = HandleValue.TTL_TYPE_RELATIVE;
                    value.ttl = ttlPrim.getAsInt();
                } else throw new NumberFormatException("bad ttl");
            }
        }

        private static String bit(boolean b) {
            return b ? "1" : "0";
        }

        private static JsonElement serializePermissions(HandleValue value) {
            return new JsonPrimitive(bit(value.adminRead) + bit(value.adminWrite) + bit(value.publicRead) + bit(value.publicWrite));
        }

        private static void deserializePermissions(HandleValue value, JsonObject obj) {
            if (obj.has("permissions")) {
                String perms = obj.get("permissions").getAsString();
                if ("*".equals(perms)) {
                    value.publicRead = value.publicWrite = value.adminRead = value.adminWrite = true;
                } else {
                    if (perms.length() > 1) value.publicWrite = '1' == perms.charAt(perms.length() - 1);
                    if (perms.length() > 2) value.publicRead = '1' == perms.charAt(perms.length() - 2);
                    if (perms.length() > 3) value.adminWrite = '1' == perms.charAt(perms.length() - 3);
                    if (perms.length() > 4) value.adminRead = '1' == perms.charAt(perms.length() - 4);
                }
            }
        }

    }

    public static class TransactionGsonTypeAdapter implements JsonSerializer, JsonDeserializer {
        @Override
        public JsonElement serialize(Transaction txn, Type typeOfSrc, JsonSerializationContext context) {
            JsonObject json = new JsonObject();
            json.addProperty("txnId", txn.txnId);
            json.addProperty("handle", Util.decodeString(txn.handle));
            json.addProperty("action", Transaction.actionToString(txn.action));
            json.addProperty("date", FastDateFormat.getUtcFormat().format(txn.date));
            return json;
        }

        @Override
        public Transaction deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
            try {
                JsonObject obj = json.getAsJsonObject();
                int txnId = obj.get("txnId").getAsInt();
                String handleString = obj.get("handle").getAsString();
                byte[] handle = Util.encodeString(handleString);
                byte action = Transaction.stringToAction(obj.get("action").getAsString());
                long date = FastDateFormat.parseUtc(obj.get("date").getAsString());
                return new Transaction(txnId, handle, action, date);
            } catch (JsonParseException e) {
                throw e;
            } catch (Exception e) {
                throw new JsonParseException(e);
            }
        }
    }

    public static class ValueReferenceGsonTypeAdapter implements JsonSerializer, JsonDeserializer {
        @Override
        public JsonElement serialize(ValueReference valueRef, Type typeOfSrc, JsonSerializationContext context) {
            JsonObject json = new JsonObject();
            json.addProperty("handle", Util.decodeString(valueRef.handle));
            json.addProperty("index", Integer.valueOf(valueRef.index));
            return json;
        }

        @Override
        public ValueReference deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
            try {
                JsonObject obj = json.getAsJsonObject();
                String handle = obj.get("handle").getAsString();
                int index = obj.get("index").getAsInt();
                return new ValueReference(Util.encodeString(handle), index);
            } catch (JsonParseException e) {
                throw e;
            } catch (Exception e) {
                throw new JsonParseException(e);
            }
        }
    }

    public static class AdminRecordGsonTypeAdapter implements JsonSerializer, JsonDeserializer {
        private static boolean[] allTrueArray = { true, true, true, true, true, true, true, true, true, true, true, true };

        private static String permsArrayToString(boolean[] perms) {
            if (perms == null) return null;
            // boolean allTrue = true;
            StringBuilder sb = new StringBuilder(perms.length);
            for (int i = perms.length - 1; i >= 0; i--) {
                // if(!perms[i]) allTrue = false;
                sb.append(perms[i] ? '1' : '0');
            }
            // if(allTrue) return "*";
            return sb.toString();
        }

        private static boolean[] permsStringToArray(String perms) {
            if ("*".equals(perms)) return allTrueArray.clone();
            boolean[] res = new boolean[12];
            if (perms == null) return res;
            for (int i = 0; i < res.length; i++) {
                if (perms.length() <= i) break;
                res[i] = '1' == perms.charAt(perms.length() - 1 - i);
            }
            return res;
        }

        private static boolean[] permsPrimitiveToArray(JsonPrimitive perms) {
            if (perms.isBoolean()) {
                if (perms.getAsBoolean()) return allTrueArray.clone();
                else return new boolean[12];
            } else if (perms.isString()) return permsStringToArray(perms.getAsString());
            else return permsStringToArray(Integer.toBinaryString(perms.getAsInt()));
        }

        private static boolean[] permsArrayToArray(JsonArray perms) {
            boolean[] res = new boolean[12];
            for (int i = 0; i < 12 && i < perms.size(); i++) {
                res[i] = isTruthy(perms.get(i));
            }
            return res;
        }

        private static boolean isTruthy(JsonElement json) {
            if (json == null || json.isJsonNull()) return false;
            if (!json.isJsonPrimitive()) return true;
            JsonPrimitive jsonPrimitive = json.getAsJsonPrimitive();
            if (jsonPrimitive.isBoolean()) return jsonPrimitive.getAsBoolean();
            if (jsonPrimitive.isString()) return !jsonPrimitive.getAsString().isEmpty();
            double num = jsonPrimitive.getAsNumber().doubleValue();
            if (Double.isNaN(num)) return false;
            if (num == 0.0) return false;
            return true;
        }

        private static boolean[] permsElementToArray(JsonElement perms) {
            if (perms == null || perms.isJsonNull()) return new boolean[12];
            else if (perms.isJsonPrimitive()) return permsPrimitiveToArray(perms.getAsJsonPrimitive());
            else if (perms.isJsonArray()) return permsArrayToArray(perms.getAsJsonArray());
            else throw new IllegalStateException("Did not expect permissions object");
        }

        @Override
        public JsonElement serialize(AdminRecord src, Type typeOfSrc, JsonSerializationContext context) {
            JsonObject json = new JsonObject();
            json.addProperty("handle", Util.decodeString(src.adminId));
            json.addProperty("index", src.adminIdIndex);
            json.addProperty("permissions", permsArrayToString(src.perms));
            if (src.legacyByteLength) json.addProperty("legacyByteLength", true);
            return json;
        }

        @Override
        public AdminRecord deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
            try {
                JsonObject obj = json.getAsJsonObject();
                String handle = obj.get("handle").getAsString();
                int index = obj.get("index").getAsInt();
                JsonElement perms = obj.get("permissions");
                boolean[] permsArray = permsElementToArray(perms);
                AdminRecord res = new AdminRecord(Util.encodeString(handle), index, permsArray[0], permsArray[1], permsArray[2], permsArray[3], permsArray[4], permsArray[5], permsArray[6], permsArray[7], permsArray[8], permsArray[9],
                    permsArray[10], permsArray[11]);
                JsonElement legacyByteLengthElement = obj.get("legacyByteLength");
                if (legacyByteLengthElement != null && legacyByteLengthElement.getAsBoolean()) res.legacyByteLength = true;
                return res;
            } catch (JsonParseException e) {
                throw e;
            } catch (Exception e) {
                throw new JsonParseException(e);
            }
        }
    }

    public static class SiteInfoGsonTypeAdapter implements JsonSerializer, JsonDeserializer {
        private static JsonElement serializeAttributes(Attribute[] attributes) {
            JsonArray json = new JsonArray();
            for (Attribute att : attributes) {
                JsonObject attJson = new JsonObject();
                attJson.addProperty("name", Util.decodeString(att.name));
                attJson.addProperty("value", Util.decodeString(att.value));
                json.add(attJson);
            }
            return json;
        }

        private static Attribute[] deserializeAttributes(JsonElement json) {
            List atts = new ArrayList<>();
            if (json.isJsonArray()) {
                for (JsonElement el : json.getAsJsonArray()) {
                    JsonObject obj = el.getAsJsonObject();
                    atts.add(new Attribute(Util.encodeString(obj.get("name").getAsString()), Util.encodeString(obj.get("value").getAsString())));
                }
            } else { //if(json.isJsonObject()) {
                for (Map.Entry entry : json.getAsJsonObject().entrySet()) {
                    if (entry.getValue().isJsonArray()) {
                        for (JsonElement el : entry.getValue().getAsJsonArray()) {
                            atts.add(new Attribute(Util.encodeString(entry.getKey()), Util.encodeString(el.getAsString())));
                        }
                    } else {
                        atts.add(new Attribute(Util.encodeString(entry.getKey()), Util.encodeString(entry.getValue().getAsString())));
                    }
                }
            }
            return atts.toArray(new Attribute[atts.size()]);
        }

        @Override
        public JsonElement serialize(SiteInfo site, Type typeOfSrc, JsonSerializationContext context) {
            JsonObject json = new JsonObject();
            json.addProperty("version", Integer.valueOf(site.dataFormatVersion));
            json.addProperty("protocolVersion", "" + Integer.valueOf(site.majorProtocolVersion) + "." + Integer.valueOf(site.minorProtocolVersion));
            json.addProperty("serialNumber", Integer.valueOf(site.serialNumber));
            json.addProperty("primarySite", Boolean.valueOf(site.isPrimary));
            json.addProperty("multiPrimary", Boolean.valueOf(site.multiPrimary));
            if (site.hashOption != SiteInfo.HASH_TYPE_BY_ALL) json.addProperty("hashOption", Integer.valueOf(site.hashOption));
            if (site.hashFilter != null && site.hashFilter.length > 0) json.addProperty("hashFilter", Util.decodeString(site.hashFilter));
            if (site.attributes != null) json.add("attributes", serializeAttributes(site.attributes));
            if (site.servers != null) json.add("servers", context.serialize(site.servers));
            return json;
        }

        @Override
        public SiteInfo deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
            try {
                JsonObject obj = json.getAsJsonObject();
                obj = lowerCaseIfNeeded(obj);
                SiteInfo site = new SiteInfo();
                if (obj.has("version")) site.dataFormatVersion = obj.get("version").getAsInt();
                if (obj.has("protocolVersion")) {
                    String versionString = obj.get("protocolVersion").getAsString();
                    int point = versionString.indexOf('.');
                    site.majorProtocolVersion = Byte.parseByte(versionString.substring(0, point));
                    site.minorProtocolVersion = Byte.parseByte(versionString.substring(point + 1));
                }
                if (obj.has("serialNumber")) site.serialNumber = obj.get("serialNumber").getAsInt();
                if (obj.has("primarySite")) site.isPrimary = obj.get("primarySite").getAsBoolean();
                if (obj.has("multiPrimary")) site.multiPrimary = obj.get("multiPrimary").getAsBoolean();
                if (obj.has("hashOption")) site.hashOption = obj.get("hashOption").getAsByte();
                if (obj.has("hashFilter")) site.hashFilter = Util.encodeString(obj.get("hashFilter").getAsString());
                if (obj.has("attributes")) site.attributes = deserializeAttributes(obj.get("attributes"));
                JsonElement servers = obj.get("servers");
                if (servers == null) servers = obj.get("serverList");
                site.servers = context.deserialize(servers, ServerInfo[].class);
                ensureNoTrailingComma(site.servers);
                return site;
            } catch (JsonParseException e) {
                throw e;
            } catch (Exception e) {
                throw new JsonParseException(e);
            }
        }
    }

    public static class ServerInfoGsonTypeAdapter implements JsonSerializer, JsonDeserializer {
        private static byte[] fill16(byte[] bytes) {
            if (bytes.length == 16) return bytes;
            byte[] res = new byte[16];
            System.arraycopy(bytes, 0, res, 16 - bytes.length, bytes.length);
            return res;
        }

        @Override
        public JsonElement serialize(ServerInfo server, Type typeOfSrc, JsonSerializationContext context) {
            JsonObject json = new JsonObject();
            json.addProperty("serverId", Integer.valueOf(server.serverId));
            json.addProperty("address", server.getAddressString());
            try {
                PublicKey key = Util.getPublicKeyFromBytes(server.publicKey);
                json.add("publicKey", dataOfType("key", context.serialize(key)));
            } catch (Exception e) {
                json.add("publicKey", serializeBinary(server.publicKey));
            }
            json.add("interfaces", context.serialize(server.interfaces));
            return json;
        }

        @Override
        public ServerInfo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
            try {
                JsonObject origObj = json.getAsJsonObject();
                JsonObject obj = lowerCaseIfNeeded(origObj);
                boolean legacy = obj != origObj;
                ServerInfo server = new ServerInfo();
                if (obj.has("serverId")) server.serverId = obj.get("serverId").getAsInt();
                server.ipAddress = fill16(InetAddress.getByName(obj.get("address").getAsString()).getAddress());
                if (obj.has("publicKey")) {
                    if (legacy && obj.get("publicKey").isJsonPrimitive()) server.publicKey = Hex.decodeHex(obj.get("publicKey").getAsString().toCharArray());
                    else server.publicKey = deserializeData(obj.get("publicKey"), context);
                }
                JsonElement interfaces = obj.get("interfaces");
                if (interfaces == null) interfaces = obj.get("interfaceList");
                server.interfaces = context.deserialize(interfaces, Interface[].class);
                ensureNoTrailingComma(server.interfaces);
                return server;
            } catch (JsonParseException e) {
                throw e;
            } catch (Exception e) {
                throw new JsonParseException(e);
            }
        }
    }

    public static class InterfaceGsonTypeAdapter implements JsonSerializer, JsonDeserializer {
        @Override
        public JsonElement serialize(Interface intf, Type typeOfSrc, JsonSerializationContext context) {
            JsonObject json = new JsonObject();
            json.addProperty("query", Boolean.valueOf((intf.type & Interface.ST_QUERY) != 0));
            json.addProperty("admin", Boolean.valueOf((intf.type & Interface.ST_ADMIN) != 0));
            serializeProtocol(json, intf.protocol);
            json.addProperty("port", Integer.valueOf(intf.port));
            return json;
        }

        private static void serializeProtocol(JsonObject json, byte protocol) {
            if (protocol == Interface.SP_HDL_UDP) json.addProperty("protocol", "UDP");
            else if (protocol == Interface.SP_HDL_TCP) json.addProperty("protocol", "TCP");
            else if (protocol == Interface.SP_HDL_HTTP) json.addProperty("protocol", "HTTP");
            else if (protocol == Interface.SP_HDL_HTTPS) json.addProperty("protocol", "HTTPS");
            else json.addProperty("protocol", Integer.valueOf(protocol));
        }

        @Override
        public Interface deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
            try {
                JsonObject obj = json.getAsJsonObject();
                obj = lowerCaseIfNeeded(obj);
                Interface intf = new Interface();
                boolean query = obj.has("query") && obj.get("query").getAsBoolean();
                boolean admin = obj.has("admin") && obj.get("admin").getAsBoolean();
                intf.type = query ? (admin ? Interface.ST_ADMIN_AND_QUERY : Interface.ST_QUERY) : (admin ? Interface.ST_ADMIN : Interface.ST_OUT_OF_SERVICE);
                String protocol = obj.get("protocol").getAsString();
                if ("UDP".equals(protocol)) intf.protocol = Interface.SP_HDL_UDP;
                else if ("TCP".equals(protocol)) intf.protocol = Interface.SP_HDL_TCP;
                else if ("HTTP".equals(protocol)) intf.protocol = Interface.SP_HDL_HTTP;
                else if ("HTTPS".equals(protocol)) intf.protocol = Interface.SP_HDL_HTTPS;
                else intf.protocol = obj.get("protocol").getAsByte();
                intf.port = obj.get("port").getAsInt();
                return intf;
            } catch (JsonParseException e) {
                throw e;
            } catch (Exception e) {
                throw new JsonParseException(e);
            }
        }
    }

    public static class ResponseGsonTypeHierarchyAdapter implements JsonSerializer {
        @Override
        public JsonElement serialize(AbstractResponse resp, Type typeOfSrc, JsonSerializationContext context) {
            JsonObject json = new JsonObject();
            //json.addProperty("opCode",Integer.valueOf(resp.opCode));
            json.addProperty("responseCode", Integer.valueOf(resp.responseCode));
            if (resp instanceof ResolutionResponse) {
                ResolutionResponse rresp = (ResolutionResponse) resp;
                json.addProperty("handle", Util.decodeString(rresp.handle));
                try {
                    HandleValue[] values = rresp.getHandleValues();
                    json.add("values", context.serialize(values));
                } catch (Exception e) {
                    json.addProperty("responseCode", Integer.valueOf(AbstractMessage.RC_ERROR));
                    json.addProperty("message", "Error decoding values of resolution response " + e.toString());
                }
            } else if (resp instanceof ServiceReferralResponse) {
                ServiceReferralResponse rresp = (ServiceReferralResponse) resp;
                if (rresp.handle.length > 0) json.addProperty("referralHandle", Util.decodeString(rresp.handle));
                if (rresp.values != null && rresp.values.length > 0) {
                    try {
                        HandleValue[] values = rresp.getHandleValues();
                        json.add("values", context.serialize(values));
                    } catch (Exception e) {
                        json.addProperty("responseCode", Integer.valueOf(AbstractMessage.RC_ERROR));
                        json.addProperty("message", "Error decoding values of resolution response " + e.toString());
                    }
                }
            } else if (resp instanceof ErrorResponse) {
                ErrorResponse eresp = (ErrorResponse) resp;
                if (eresp.message != null && eresp.message.length > 0) {
                    json.addProperty("message", Util.decodeString(eresp.message));
                }
                if (resp.opCode == AbstractMessage.OC_RESOLUTION && resp.responseCode == AbstractMessage.RC_VALUES_NOT_FOUND) {
                    json.add("values", new JsonArray());
                }
            } else if (resp instanceof GetSiteInfoResponse) {
                GetSiteInfoResponse gresp = (GetSiteInfoResponse) resp;
                json.add("site", context.serialize(gresp.siteInfo));
            } else if (resp instanceof VerifyAuthResponse) {
                json.addProperty("isValid", Boolean.valueOf(((VerifyAuthResponse) resp).isValid));
            } else if (resp instanceof CreateHandleResponse) {
                CreateHandleResponse creResp = (CreateHandleResponse) resp;
                if (creResp.handle != null) {
                    json.addProperty("handle", Util.decodeString(creResp.handle));
                }
            }
            return json;
        }
    }

    public static class PublicKeyTypeHierarchyAdapter implements JsonSerializer, JsonDeserializer {
        @Override
        public JsonElement serialize(PublicKey key, Type typeOfSrc, JsonSerializationContext context) {
            JsonObject json = new JsonObject();
            if (key instanceof DSAPublicKey) {
                DSAPublicKey dsaKey = (DSAPublicKey) key;
                byte[] y = dsaKey.getY().toByteArray();
                DSAParams dsaParams = dsaKey.getParams();
                byte[] p = dsaParams.getP().toByteArray();
                byte[] q = dsaParams.getQ().toByteArray();
                byte[] g = dsaParams.getG().toByteArray();
                json.addProperty("kty", "DSA");
                json.addProperty("y", Base64.encodeBase64URLSafeString(unsigned(y)));
                json.addProperty("p", Base64.encodeBase64URLSafeString(unsigned(p)));
                json.addProperty("q", Base64.encodeBase64URLSafeString(unsigned(q)));
                json.addProperty("g", Base64.encodeBase64URLSafeString(unsigned(g)));
            } else if (key instanceof RSAPublicKey) {
                RSAPublicKey rsaKey = (RSAPublicKey) key;
                byte[] n = rsaKey.getModulus().toByteArray();
                byte[] e = rsaKey.getPublicExponent().toByteArray();
                json.addProperty("kty", "RSA");
                json.addProperty("n", Base64.encodeBase64URLSafeString(unsigned(n)));
                json.addProperty("e", Base64.encodeBase64URLSafeString(unsigned(e)));
            } else {
                throw new UnsupportedOperationException("Unsupported key type " + key.getClass().getName());
            }
            return json;
        }

        @Override
        public PublicKey deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
            try {
                JsonObject obj = json.getAsJsonObject();
                String kty = obj.get("kty").getAsString();
                if ("DSA".equalsIgnoreCase(kty)) {
                    byte[] y = Base64.decodeBase64(obj.get("y").getAsString());
                    byte[] p = Base64.decodeBase64(obj.get("p").getAsString());
                    byte[] q = Base64.decodeBase64(obj.get("q").getAsString());
                    byte[] g = Base64.decodeBase64(obj.get("g").getAsString());
                    DSAPublicKeySpec keySpec = new DSAPublicKeySpec(new BigInteger(1, y), new BigInteger(1, p), new BigInteger(1, q), new BigInteger(1, g));
                    KeyFactory dsaKeyFactory = KeyFactory.getInstance("DSA");
                    return dsaKeyFactory.generatePublic(keySpec);
                } else if ("RSA".equalsIgnoreCase(kty)) {
                    byte[] n = Base64.decodeBase64(obj.get("n").getAsString());
                    byte[] e = Base64.decodeBase64(obj.get("e").getAsString());
                    RSAPublicKeySpec keySpec = new RSAPublicKeySpec(new BigInteger(1, n), new BigInteger(1, e));
                    KeyFactory rsaKeyFactory = KeyFactory.getInstance("RSA");
                    return rsaKeyFactory.generatePublic(keySpec);
                } else {
                    throw new UnsupportedOperationException("Unsupported key type " + kty);
                }
            } catch (JsonParseException e) {
                throw e;
            } catch (Exception e) {
                throw new JsonParseException(e);
            }
        }
    }

    public static class PrivateKeyTypeHierarchyAdapter implements JsonSerializer, JsonDeserializer {
        @Override
        public JsonElement serialize(PrivateKey key, Type typeOfSrc, JsonSerializationContext context) {
            JsonObject json = new JsonObject();
            if (key instanceof DSAPrivateKey) {
                DSAPrivateKey dsaKey = (DSAPrivateKey) key;
                byte[] x = dsaKey.getX().toByteArray();
                DSAParams dsaParams = dsaKey.getParams();
                byte[] p = dsaParams.getP().toByteArray();
                byte[] q = dsaParams.getQ().toByteArray();
                byte[] g = dsaParams.getG().toByteArray();
                json.addProperty("kty", "DSA");
                json.addProperty("x", Base64.encodeBase64URLSafeString(unsigned(x)));
                json.addProperty("p", Base64.encodeBase64URLSafeString(unsigned(p)));
                json.addProperty("q", Base64.encodeBase64URLSafeString(unsigned(q)));
                json.addProperty("g", Base64.encodeBase64URLSafeString(unsigned(g)));
            } else if (key instanceof RSAPrivateKey) {
                RSAPrivateKey rsaKey = (RSAPrivateKey) key;
                byte[] n = rsaKey.getModulus().toByteArray();
                byte[] d = rsaKey.getPrivateExponent().toByteArray();
                json.addProperty("kty", "RSA");
                if (key instanceof RSAPrivateCrtKey) {
                    RSAPrivateCrtKey rsacrtKey = (RSAPrivateCrtKey) rsaKey;
                    byte[] e = rsacrtKey.getPublicExponent().toByteArray();
                    byte[] p = rsacrtKey.getPrimeP().toByteArray();
                    byte[] q = rsacrtKey.getPrimeQ().toByteArray();
                    byte[] dp = rsacrtKey.getPrimeExponentP().toByteArray();
                    byte[] dq = rsacrtKey.getPrimeExponentQ().toByteArray();
                    byte[] qi = rsacrtKey.getCrtCoefficient().toByteArray();
                    json.addProperty("n", Base64.encodeBase64URLSafeString(unsigned(n)));
                    json.addProperty("e", Base64.encodeBase64URLSafeString(unsigned(e)));
                    json.addProperty("d", Base64.encodeBase64URLSafeString(unsigned(d)));
                    json.addProperty("p", Base64.encodeBase64URLSafeString(unsigned(p)));
                    json.addProperty("q", Base64.encodeBase64URLSafeString(unsigned(q)));
                    json.addProperty("dp", Base64.encodeBase64URLSafeString(unsigned(dp)));
                    json.addProperty("dq", Base64.encodeBase64URLSafeString(unsigned(dq)));
                    json.addProperty("qi", Base64.encodeBase64URLSafeString(unsigned(qi)));
                } else {
                    json.addProperty("n", Base64.encodeBase64URLSafeString(unsigned(n)));
                    json.addProperty("d", Base64.encodeBase64URLSafeString(unsigned(d)));
                }
            } else {
                throw new UnsupportedOperationException("Unsupported key type " + key.getClass().getName());
            }
            return json;
        }

        @Override
        public PrivateKey deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
            try {
                JsonObject obj = json.getAsJsonObject();
                String kty = obj.get("kty").getAsString();
                if ("DSA".equalsIgnoreCase(kty)) {
                    byte[] x = Base64.decodeBase64(obj.get("x").getAsString());
                    byte[] p = Base64.decodeBase64(obj.get("p").getAsString());
                    byte[] q = Base64.decodeBase64(obj.get("q").getAsString());
                    byte[] g = Base64.decodeBase64(obj.get("g").getAsString());
                    DSAPrivateKeySpec keySpec = new DSAPrivateKeySpec(new BigInteger(1, x), new BigInteger(1, p), new BigInteger(1, q), new BigInteger(1, g));
                    KeyFactory dsaKeyFactory = KeyFactory.getInstance("DSA");
                    return dsaKeyFactory.generatePrivate(keySpec);
                } else if ("RSA".equalsIgnoreCase(kty)) {
                    byte[] n = Base64.decodeBase64(obj.get("n").getAsString());
                    byte[] d = Base64.decodeBase64(obj.get("d").getAsString());
                    RSAPrivateKeySpec keySpec;
                    if (obj.has("qi")) {
                        byte[] e = Base64.decodeBase64(obj.get("e").getAsString());
                        byte[] p = Base64.decodeBase64(obj.get("p").getAsString());
                        byte[] q = Base64.decodeBase64(obj.get("q").getAsString());
                        byte[] dp = Base64.decodeBase64(obj.get("dp").getAsString());
                        byte[] dq = Base64.decodeBase64(obj.get("dq").getAsString());
                        byte[] qi = Base64.decodeBase64(obj.get("qi").getAsString());
                        keySpec = new RSAPrivateCrtKeySpec(new BigInteger(1, n), new BigInteger(1, e), new BigInteger(1, d), new BigInteger(1, p), new BigInteger(1, q), new BigInteger(1, dp), new BigInteger(1, dq), new BigInteger(1, qi));
                    } else {
                        keySpec = new RSAPrivateKeySpec(new BigInteger(1, n), new BigInteger(1, d));
                    }
                    KeyFactory rsaKeyFactory = KeyFactory.getInstance("RSA");
                    return rsaKeyFactory.generatePrivate(keySpec);
                } else {
                    throw new UnsupportedOperationException("Unsupported key type " + kty);
                }
            } catch (JsonParseException e) {
                throw e;
            } catch (Exception e) {
                throw new JsonParseException(e);
            }
        }
    }

    private static byte[] unsigned(byte[] arr) {
        if (arr.length == 0) return new byte[1];
        int zeros = 0;
        for (byte element : arr) {
            if (element == 0) zeros++;
            else break;
        }
        if (zeros == arr.length) zeros--;
        if (zeros == 0) return arr;
        byte[] res = new byte[arr.length - zeros];
        System.arraycopy(arr, zeros, res, 0, arr.length - zeros);
        return res;
    }

    private static  void ensureNoTrailingComma(T[] arr) {
        if (arr == null || arr.length == 0) return;
        if (arr[arr.length - 1] == null) throw new JsonParseException("While parsing JSON found array ending with null");
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy