JSONPathFunction.java

package com.alibaba.fastjson2;

import com.alibaba.fastjson2.function.impl.ToDouble;

import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.function.BiFunction;
import java.util.function.Function;

final class JSONPathFunction
        extends JSONPathSegment
        implements JSONPathSegment.EvalSegment {
    static JSONPathFunction FUNC_TYPE = new JSONPathFunction(JSONPathFunction::type);
    static JSONPathFunction FUNC_DOUBLE = new JSONPathFunction(new ToDouble(null));
    static JSONPathFunction FUNC_FLOOR = new JSONPathFunction(JSONPathFunction::floor);
    static JSONPathFunction FUNC_CEIL = new JSONPathFunction(JSONPathFunction::ceil);
    static JSONPathFunction FUNC_ABS = new JSONPathFunction(JSONPathFunction::abs);
    static JSONPathFunction FUNC_NEGATIVE = new JSONPathFunction(JSONPathFunction::negative);
    static JSONPathFunction FUNC_EXISTS = new JSONPathFunction(JSONPathFunction::exists);
    static JSONPathFunction FUNC_LOWER = new JSONPathFunction(JSONPathFunction::lower);
    static JSONPathFunction FUNC_UPPER = new JSONPathFunction(JSONPathFunction::upper);
    static JSONPathFunction FUNC_TRIM = new JSONPathFunction(JSONPathFunction::trim);

    final Function function;

    public JSONPathFunction(Function function) {
        this.function = function;
    }

    static Object floor(Object value) {
        if (value instanceof Double) {
            return Math.floor((Double) value);
        }

        if (value instanceof Float) {
            return Math.floor((Float) value);
        }

        if (value instanceof BigDecimal) {
            return ((BigDecimal) value).setScale(0, RoundingMode.FLOOR);
        }

        if (value instanceof List) {
            List list = (List) value;
            for (int i = 0, l = list.size(); i < l; i++) {
                Object item = list.get(i);
                if (item instanceof Double) {
                    list.set(i, Math.floor((Double) item));
                } else if (item instanceof Float) {
                    list.set(i, Math.floor((Float) item));
                } else if (item instanceof BigDecimal) {
                    list.set(i, ((BigDecimal) item).setScale(0, RoundingMode.FLOOR));
                }
            }
        }

        return value;
    }

    static Object ceil(Object value) {
        if (value instanceof Double) {
            return Math.ceil((Double) value);
        }

        if (value instanceof Float) {
            return Math.ceil((Float) value);
        }

        if (value instanceof BigDecimal) {
            return ((BigDecimal) value).setScale(0, RoundingMode.CEILING);
        }

        if (value instanceof List) {
            List list = (List) value;
            for (int i = 0, l = list.size(); i < l; i++) {
                Object item = list.get(i);
                if (item instanceof Double) {
                    list.set(i, Math.ceil((Double) item));
                } else if (item instanceof Float) {
                    list.set(i, Math.ceil((Float) item));
                } else if (item instanceof BigDecimal) {
                    list.set(i, ((BigDecimal) item).setScale(0, RoundingMode.CEILING));
                }
            }
        }

        return value;
    }

    static Object exists(Object value) {
        return value != null;
    }

    static Object negative(Object value) {
        if (value == null) {
            return null;
        }

        if (value instanceof Integer) {
            int intValue = ((Integer) value).intValue();
            if (intValue == Integer.MIN_VALUE) {
                return -(long) intValue;
            }
            return -intValue;
        }

        if (value instanceof Long) {
            long longValue = ((Long) value).longValue();
            if (longValue == Long.MIN_VALUE) {
                return BigInteger.valueOf(longValue).negate();
            }
            return -longValue;
        }

        if (value instanceof Byte) {
            byte byteValue = ((Byte) value).byteValue();
            if (byteValue == Byte.MIN_VALUE) {
                return -(short) byteValue;
            }

            return (byte) -byteValue;
        }

        if (value instanceof Short) {
            short shortValue = ((Short) value).shortValue();
            if (shortValue == Short.MIN_VALUE) {
                return -(int) shortValue;
            }

            return (short) -shortValue;
        }

        if (value instanceof Double) {
            return -((Double) value).doubleValue();
        }

        if (value instanceof Float) {
            return -((Float) value).floatValue();
        }

        if (value instanceof BigDecimal) {
            return ((BigDecimal) value).negate();
        }

        if (value instanceof BigInteger) {
            return ((BigInteger) value).negate();
        }

        if (value instanceof List) {
            List list = (List) value;
            JSONArray values = new JSONArray(list.size());
            for (int i = 0, l = list.size(); i < l; i++) {
                Object item = list.get(i);
                Object negativeItem = negative(item);
                values.add(negativeItem);
            }
            return values;
        }

        return value;
    }

    static Object abs(Object value) {
        if (value == null) {
            return null;
        }

        if (value instanceof Integer) {
            int intValue = ((Integer) value).intValue();
            if (intValue < 0) {
                return -intValue;
            }
            return value;
        }

        if (value instanceof Long) {
            long longValue = ((Long) value).longValue();
            if (longValue < 0) {
                return -longValue;
            }
            return value;
        }

        if (value instanceof Byte) {
            byte byteValue = ((Byte) value).byteValue();
            if (byteValue < 0) {
                return (byte) -byteValue;
            }

            return value;
        }

        if (value instanceof Short) {
            short shortValue = ((Short) value).shortValue();
            if (shortValue < 0) {
                return (short) -shortValue;
            }

            return value;
        }

        if (value instanceof Double) {
            double doubleValue = ((Double) value).doubleValue();
            if (doubleValue < 0) {
                return -doubleValue;
            }

            return value;
        }

        if (value instanceof Float) {
            float floatValue = ((Float) value).floatValue();
            if (floatValue < 0) {
                return -floatValue;
            }

            return value;
        }

        if (value instanceof BigDecimal) {
            return ((BigDecimal) value).abs();
        }

        if (value instanceof BigInteger) {
            return ((BigInteger) value).abs();
        }

        if (value instanceof List) {
            List list = (List) value;
            JSONArray values = new JSONArray(list.size());
            for (int i = 0, l = list.size(); i < l; i++) {
                Object item = list.get(i);
                values.add(abs(item));
            }
            return values;
        }

        throw new JSONException("abs not support " + value);
    }

    static String type(Object value) {
        if (value == null) {
            return "null";
        }

        if (value instanceof Collection) {
            return "array";
        }

        if (value instanceof Number) {
            return "number";
        }

        if (value instanceof Boolean) {
            return "boolean";
        }

        if (value instanceof String
                || value instanceof UUID
                || value instanceof Enum) {
            return "string";
        }

        return "object";
    }

    static Object lower(Object value) {
        if (value == null) {
            return null;
        }
        String str;
        if (value instanceof String) {
            str = (String) value;
        } else {
            str = value.toString();
        }
        return str.toLowerCase();
    }

    static Object upper(Object value) {
        if (value == null) {
            return null;
        }
        String str;
        if (value instanceof String) {
            str = (String) value;
        } else {
            str = value.toString();
        }
        return str.toUpperCase();
    }

    static Object trim(Object value) {
        if (value == null) {
            return null;
        }
        String str;
        if (value instanceof String) {
            str = (String) value;
        } else {
            str = value.toString();
        }
        return str.trim();
    }

    @Override
    public void accept(JSONReader jsonReader, JSONPath.Context context) {
        if (context.parent == null) {
            context.root = jsonReader.readAny();
            context.eval = true;
        }
        eval(context);
    }

    @Override
    public void eval(JSONPath.Context context) {
        Object value = context.parent == null
                ? context.root
                : context.parent.value;

        context.value = function.apply(value);
    }

    static final class TypeFunction
            implements Function {
        static final TypeFunction INSTANCE = new TypeFunction();

        @Override
        public Object apply(Object object) {
            return type(object);
        }
    }

    static final class SizeFunction
            implements Function {
        static final SizeFunction INSTANCE = new SizeFunction();

        @Override
        public Object apply(Object value) {
            if (value == null) {
                return -1;
            }

            if (value instanceof Collection) {
                return ((Collection<?>) value).size();
            }

            if (value.getClass().isArray()) {
                return Array.getLength(value);
            }

            if (value instanceof Map) {
                return ((Map) value).size();
            }

            if (value instanceof JSONPath.Sequence) {
                return ((JSONPath.Sequence) value).values.size();
            }

            return 1;
        }
    }

    static final class BiFunctionAdapter
            implements BiFunction {
        private final Function function;

        BiFunctionAdapter(Function function) {
            this.function = function;
        }

        @Override
        public Object apply(Object o1, Object o2) {
            return function.apply(o2);
        }
    }
}