protected static T doCast()

in core/src/main/java/org/apache/brooklyn/feed/http/JsonFunctions.java [356:457]


    protected static <T> T doCast(JsonElement input, TypeToken<T> expectedType) {
        if (input == null) {
            return null;
        } else if (input.isJsonNull()) {
            return null;
        }
        Class<? super T> expected = expectedType.getRawType();
        Function<Function<JsonPrimitive,Boolean>, Boolean> handlePrimitive = fn -> {
            if (Object.class.equals(expected) && input.isJsonPrimitive()) return fn.apply((JsonPrimitive) input);
            return false;
        };
        if (expected == boolean.class || expected == Boolean.class || handlePrimitive.apply(JsonPrimitive::isBoolean)) {
            return (T) (Boolean) input.getAsBoolean();
        } else if (expected == char.class || expected == Character.class) {
            return (T) (Character) input.getAsCharacter();
        } else if (expected == byte.class || expected == Byte.class) {
            return (T) (Byte) input.getAsByte();
        } else if (expected == short.class || expected == Short.class) {
            return (T) (Short) input.getAsShort();
        } else if (expected == int.class || expected == Integer.class) {
            return (T) (Integer) input.getAsInt();
        } else if (expected == long.class || expected == Long.class) {
            return (T) (Long) input.getAsLong();
        } else if (expected == float.class || expected == Float.class) {
            return (T) (Float) input.getAsFloat();
        } else if (expected == double.class || expected == Double.class) {
            return (T) (Double) input.getAsDouble();
        } else if (expected == BigDecimal.class) {
            return (T) input.getAsBigDecimal();
        } else if (expected == BigInteger.class) {
            return (T) input.getAsBigInteger();
        } else if (Number.class.isAssignableFrom(expected) || handlePrimitive.apply(JsonPrimitive::isNumber)) {
            // May result in a class-cast if it's an unexpected sub-type of Number not handled above
            // Also ends up as LazilyParsedNumber which we probably don't want
            Number result = input.getAsNumber();
            Number r2 = new NumberMath(result, Number.class).asTypeForced(Number.class);
            if (r2==null) r2 = result;
            return (T) r2;
        } else if (expected == String.class || handlePrimitive.apply(JsonPrimitive::isString)) {
            return (T) input.getAsString();
        }

        // now complex types
        if (JsonElement.class.isAssignableFrom(expected)) {
            return (T) input;
        }

        if (Iterable.class.isAssignableFrom(expected) || expected.isArray()) {
            JsonArray array = input.getAsJsonArray();
            MutableList ml = MutableList.of();
            TypeToken<?> componentType;
            if (expectedType.getComponentType()!=null) componentType = expectedType.getComponentType();
            else {
                TypeToken<?>[] params = TypeTokens.getGenericParameterTypeTokens(expectedType);
                componentType = params != null && params.length == 1 ? params[0] : TypeToken.of(Object.class);
            }

            if (JsonElement.class.isAssignableFrom(componentType.getRawType())) ml.addAll(array);
            else array.forEach(a -> ml.add(doCast(a, componentType)));

            if (expected.isAssignableFrom(MutableList.class)) {
                return (T) ml;
            }
            if (expected.isAssignableFrom(MutableSet.class)) {
                return (T) MutableSet.copyOf(ml);
            }
            if (expected.isArray()) {
                return (T) ml.toArray((Object[]) Array.newInstance(componentType.getRawType(), 0));
            }
        }

        if (Map.class.isAssignableFrom(expected)) {
            JsonObject jo = input.getAsJsonObject();

            TypeToken<?>[] params = TypeTokens.getGenericParameterTypeTokens(expectedType);
            TypeToken<?> value;
            if (params!=null && params.length==1) {
                // probably shouldn't happen? but if we supported other maps it might
                value = params[0];
            } else if (params!=null && params.length==2) {
                value = params[1];
                TypeToken<?> key = params[0];
                if (!TypeTokens.isAssignableFromRaw(key, String.class)) throw new IllegalArgumentException("Keys of type "+key+" not supported when deserializing JSON");
            } else {
                value = TypeToken.of(Object.class);
            }

            Map mm = MutableMap.of();
            jo.entrySet().forEach(jos -> mm.put(jos.getKey(), doCast(jos.getValue(), value)));
            if (expected.isAssignableFrom(MutableMap.class)) {
                return (T) mm;
            }
        }

        if (Object.class.equals(expected)) {
            // primitives should have beenhandled above
            if (input.isJsonObject()) return (T) doCast(input, Map.class);
            if (input.isJsonArray()) return (T) doCast(input, List.class);
        }

        throw new IllegalArgumentException("Cannot cast json element to type "+expected);
    }