FieldReaderList.java

package com.alibaba.fastjson2.reader;

import com.alibaba.fastjson2.JSONB;
import com.alibaba.fastjson2.JSONException;
import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.schema.JSONSchema;
import com.alibaba.fastjson2.util.Fnv;
import com.alibaba.fastjson2.util.TypeUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Supplier;

public class FieldReaderList<T, V>
        extends FieldReaderObject<T> {
    final long fieldClassHash;
    final long itemClassHash;
    final Supplier<List<V>> listCreator;
    final ObjectReader<V> itemObjectReader;

    public FieldReaderList(
            String fieldName,
            Type fieldType,
            Class fieldClass,
            Type itemType,
            Class itemClass,
            int ordinal,
            long features,
            String format,
            Locale locale,
            Object defaultValue,
            JSONSchema schema,
            Method method,
            Field field,
            BiConsumer function,
            String paramName,
            Parameter parameter,
            Supplier<List<V>> listCreator,
            ObjectReader<V> itemObjectReader
    ) {
        super(fieldName, fieldType, fieldClass, ordinal, features, format, locale, defaultValue, schema, method, field, function, paramName, parameter);
        this.itemType = itemType;
        this.itemClass = itemClass;
        this.itemClassHash = this.itemClass == null ? 0 : Fnv.hashCode64(itemClass.getName());
        this.fieldClassHash = fieldClass == null ? 0 : Fnv.hashCode64(TypeUtils.getTypeName(fieldClass));
        this.listCreator = listCreator;
        this.itemObjectReader = itemObjectReader;

        if (format != null) {
            if (itemType == Date.class) {
                itemReader = new ObjectReaderImplDate(format, locale);
            }
        }
    }

    @Override
    public long getItemClassHash() {
        return itemClassHash;
    }

    public Collection<V> createList(JSONReader.Context context) {
        if (listCreator != null) {
            return listCreator.get();
        }

        if (fieldClass == List.class || fieldClass == Collection.class || fieldClass == ArrayList.class) {
            return new ArrayList<>();
        }

        return (Collection<V>) getObjectReader(context).createInstance(features);
    }

    @Override
    public void readFieldValue(JSONReader jsonReader, T object) {
        if (jsonReader.jsonb) {
            readFieldValueJSONB(jsonReader, object);
            return;
        }

        if (jsonReader.nextIfNull()) {
            accept(object, null);
            return;
        }

        if (jsonReader.isReference()) {
            String reference = jsonReader.readReference();
            if ("..".equals(reference)) {
                accept(object, object);
            } else {
                addResolveTask(jsonReader, object, reference);
            }
            return;
        }

        JSONReader.Context context = jsonReader.getContext();
        ObjectReader objectReader = getObjectReader(context);

        Function builder = null;

        if (initReader != null) {
            builder = this.initReader.getBuildFunction();
        } else {
            if (objectReader instanceof ObjectReaderImplList) {
                builder = objectReader.getBuildFunction();
            }
        }

        char current = jsonReader.current();
        if (current == '[') {
            ObjectReader itemObjectReader = getItemObjectReader(context);

            Collection list = createList(context);
            jsonReader.next();
            for (int i = 0; ; ++i) {
                if (jsonReader.nextIfArrayEnd()) {
                    break;
                }

                if (jsonReader.readReference(list, i)) {
                    continue;
                }

                Object value;
                if (jsonReader.isString() && itemObjectReader instanceof ObjectReaderInterface) {
                    value = jsonReader.readString();
                } else {
                    value = itemObjectReader.readObject(jsonReader, null, null, 0);
                }
                list.add(value);

                jsonReader.nextIfComma();
            }
            if (builder != null) {
                list = (Collection) builder.apply(list);
            }
            accept(object, list);

            jsonReader.nextIfComma();
            return;
        } else if (current == '{' && getItemObjectReader(context) instanceof ObjectReaderBean) {
            Object itemValue = jsonReader.jsonb
                    ? itemReader.readJSONBObject(jsonReader, null, null, features)
                    : itemReader.readObject(jsonReader, null, null, features);
            Collection list = (Collection) objectReader.createInstance(features);
            list.add(itemValue);
            if (builder != null) {
                list = (Collection) builder.apply(list);
            }
            accept(object, list);

            jsonReader.nextIfComma();
            return;
        }

        Object value = jsonReader.jsonb
                ? objectReader.readJSONBObject(jsonReader, null, null, features)
                : objectReader.readObject(jsonReader, null, null, features);
        accept(object, value);
    }

    @Override
    public Object readFieldValue(JSONReader jsonReader) {
        if (jsonReader.jsonb) {
            int entryCnt = jsonReader.startArray();
            if (entryCnt == -1) {
                return null;
            }
            Object[] array = new Object[entryCnt];
            ObjectReader itemObjectReader
                    = getItemObjectReader(
                    jsonReader.getContext());
            for (int i = 0; i < entryCnt; ++i) {
                array[i] = itemObjectReader.readObject(jsonReader, null, null, 0);
            }
            return Arrays.asList(array);
        }

        if (jsonReader.current() == '[') {
            JSONReader.Context ctx = jsonReader.getContext();
            ObjectReader itemObjectReader = getItemObjectReader(ctx);

            Collection list = createList(ctx);
            jsonReader.next();
            while (!jsonReader.nextIfArrayEnd()) {
                list.add(
                        itemObjectReader.readObject(jsonReader, fieldType, fieldName, 0)
                );

                jsonReader.nextIfComma();
            }

            jsonReader.nextIfComma();

            return list;
        }

        if (jsonReader.isString()) {
            String str = jsonReader.readString();
            if (itemType instanceof Class
                    && Number.class.isAssignableFrom((Class<?>) itemType)
            ) {
                Function typeConvert = jsonReader.getContext().getProvider().getTypeConvert(String.class, itemType);
                if (typeConvert != null) {
                    Collection list = createList(jsonReader.getContext());

                    if (str.indexOf(',') != -1) {
                        String[] items = str.split(",");

                        for (String item : items) {
                            Object converted = typeConvert.apply(item);
                            list.add(converted);
                        }
                    }

                    return list;
                }
            }
        }

        throw new JSONException(jsonReader.info("TODO : " + this.getClass()));
    }

    @Override
    public ObjectReader checkObjectAutoType(JSONReader jsonReader) {
        if (jsonReader.nextIfMatch(JSONB.Constants.BC_TYPED_ANY)) {
            long typeHash = jsonReader.readTypeHashCode();
            final long features = jsonReader.features(this.features);
            JSONReader.Context context = jsonReader.getContext();
            JSONReader.AutoTypeBeforeHandler autoTypeFilter = context.getContextAutoTypeBeforeHandler();
            if (autoTypeFilter != null) {
                Class<?> filterClass = autoTypeFilter.apply(typeHash, fieldClass, features);
                if (filterClass == null) {
                    String typeName = jsonReader.getString();
                    filterClass = autoTypeFilter.apply(typeName, fieldClass, features);
                }
                if (filterClass != null) {
                    return context.getObjectReader(fieldClass);
                }
            }

            boolean isSupportAutoType = jsonReader.isSupportAutoType(features);
            if (!isSupportAutoType) {
                if (jsonReader.isArray() && !jsonReader.isEnabled(JSONReader.Feature.ErrorOnNotSupportAutoType)) {
                    return getObjectReader(jsonReader);
                }

                throw new JSONException(jsonReader.info("autoType not support input " + jsonReader.getString()));
            }

            ObjectReader autoTypeObjectReader = jsonReader.getObjectReaderAutoType(typeHash, fieldClass, features);

            if (autoTypeObjectReader instanceof ObjectReaderImplList) {
                ObjectReaderImplList listReader = (ObjectReaderImplList) autoTypeObjectReader;

                autoTypeObjectReader = new ObjectReaderImplList(fieldType, fieldClass, listReader.instanceType, itemType, listReader.builder);
            }

            if (autoTypeObjectReader == null) {
                throw new JSONException(jsonReader.info("autoType not support : " + jsonReader.getString()));
            }

            return autoTypeObjectReader;
        }
        return null;
    }
}