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

com.jsoniter.spi.ClassDescriptor Maven / Gradle / Ivy

Go to download

jsoniter (json-iterator) is fast and flexible JSON parser available in Java and Go

There is a newer version: 0.9.23
Show newest version
package com.jsoniter.spi;

import java.lang.reflect.*;
import java.util.*;

public class ClassDescriptor {

    public ClassInfo classInfo;
    public Class clazz;
    public Map lookup;
    public ConstructorDescriptor ctor;
    public List fields;
    public List setters;
    public List getters;
    public List bindingTypeWrappers;
    public List keyValueTypeWrappers;
    public List unwrappers;
    public boolean asExtraForUnknownProperties;
    public Binding onMissingProperties;
    public Binding onExtraProperties;

    private ClassDescriptor() {
    }

    public static ClassDescriptor getDecodingClassDescriptor(ClassInfo classInfo, boolean includingPrivate) {
        Class clazz = classInfo.clazz;
        Map lookup = collectTypeVariableLookup(clazz);
        ClassDescriptor desc = new ClassDescriptor();
        desc.classInfo = classInfo;
        desc.clazz = clazz;
        desc.lookup = lookup;
        desc.ctor = getCtor(clazz);
        desc.fields = getFields(lookup, classInfo, includingPrivate);
        desc.setters = getSetters(lookup, classInfo, includingPrivate);
        desc.getters = new ArrayList();
        desc.bindingTypeWrappers = new ArrayList();
        desc.keyValueTypeWrappers = new ArrayList();
        desc.unwrappers = new ArrayList();
        for (Extension extension : JsoniterSpi.getExtensions()) {
            extension.updateClassDescriptor(desc);
        }
        for (Binding field : desc.fields) {
            if (field.valueType instanceof Class) {
                Class valueClazz = (Class) field.valueType;
                if (valueClazz.isArray()) {
                    field.valueCanReuse = false;
                    continue;
                }
            }
            field.valueCanReuse = field.valueTypeLiteral.nativeType == null;
        }
        decodingDeduplicate(desc);
        if (includingPrivate) {
            if (desc.ctor.ctor != null) {
                desc.ctor.ctor.setAccessible(true);
            }
            if (desc.ctor.staticFactory != null) {
                desc.ctor.staticFactory.setAccessible(true);
            }
            for (WrapperDescriptor setter : desc.bindingTypeWrappers) {
                setter.method.setAccessible(true);
            }
        }
        for (Binding binding : desc.allDecoderBindings()) {
            if (binding.fromNames == null) {
                binding.fromNames = new String[]{binding.name};
            }
            if (binding.field != null && includingPrivate) {
                binding.field.setAccessible(true);
            }
            if (binding.method != null && includingPrivate) {
                binding.method.setAccessible(true);
            }
            if (binding.decoder != null) {
                JsoniterSpi.addNewDecoder(binding.decoderCacheKey(), binding.decoder);
            }
        }
        return desc;
    }

    public static ClassDescriptor getEncodingClassDescriptor(ClassInfo classInfo, boolean includingPrivate) {
        Class clazz = classInfo.clazz;
        Map lookup = collectTypeVariableLookup(clazz);
        ClassDescriptor desc = new ClassDescriptor();
        desc.classInfo = classInfo;
        desc.clazz = clazz;
        desc.lookup = lookup;
        desc.fields = getFields(lookup, classInfo, includingPrivate);
        desc.getters = getGetters(lookup, classInfo, includingPrivate);
        desc.bindingTypeWrappers = new ArrayList();
        desc.keyValueTypeWrappers = new ArrayList();
        desc.unwrappers = new ArrayList();
        for (Extension extension : JsoniterSpi.getExtensions()) {
            extension.updateClassDescriptor(desc);
        }
        encodingDeduplicate(desc);
        for (Binding binding : desc.allEncoderBindings()) {
            if (binding.toNames == null) {
                binding.toNames = new String[]{binding.name};
            }
            if (binding.field != null && includingPrivate) {
                binding.field.setAccessible(true);
            }
            if (binding.method != null && includingPrivate) {
                binding.method.setAccessible(true);
            }
            if (binding.encoder != null) {
                JsoniterSpi.addNewEncoder(binding.encoderCacheKey(), binding.encoder);
            }
        }
        return desc;
    }

    private static void decodingDeduplicate(ClassDescriptor desc) {
        HashMap byName = new HashMap();
        for (Binding field : desc.fields) {
            for (String fromName : field.fromNames) {
                if (byName.containsKey(fromName)) {
                    throw new JsonException("field decode from same name: " + fromName);
                }
                byName.put(fromName, field);
            }
        }
        ArrayList iteratingSetters = new ArrayList(desc.setters);
        Collections.reverse(iteratingSetters);
        for (Binding setter : iteratingSetters) {
            for (String fromName : setter.fromNames) {
                Binding existing = byName.get(fromName);
                if (existing == null) {
                    byName.put(fromName, setter);
                    continue;
                }
                if (desc.fields.remove(existing)) {
                    continue;
                }
                if (existing.method != null && existing.method.getName().equals(setter.method.getName())) {
                    // inherited interface setter
                    // iterate in reverse order, so that the setter from child class will be kept
                    desc.setters.remove(existing);
                    continue;
                }
                throw new JsonException("setter decode from same name: " + fromName);
            }
        }
        for (WrapperDescriptor wrapper : desc.bindingTypeWrappers) {
            for (Binding param : wrapper.parameters) {
                for (String fromName : param.fromNames) {
                    Binding existing = byName.get(fromName);
                    if (existing == null) {
                        byName.put(fromName, param);
                        continue;
                    }
                    if (desc.fields.remove(existing)) {
                        continue;
                    }
                    if (desc.setters.remove(existing)) {
                        continue;
                    }
                    throw new JsonException("wrapper parameter decode from same name: " + fromName);
                }
            }
        }
        for (Binding param : desc.ctor.parameters) {
            for (String fromName : param.fromNames) {
                Binding existing = byName.get(fromName);
                if (existing == null) {
                    byName.put(fromName, param);
                    continue;
                }
                if (desc.fields.remove(existing)) {
                    continue;
                }
                if (desc.setters.remove(existing)) {
                    continue;
                }
                throw new JsonException("ctor parameter decode from same name: " + fromName);
            }
        }
    }

    private static void encodingDeduplicate(ClassDescriptor desc) {
        HashMap byName = new HashMap();
        for (Binding field : desc.fields) {
            for (String toName : field.toNames) {
                if (byName.containsKey(toName)) {
                    throw new JsonException("field encode to same name: " + toName);
                }
                byName.put(toName, field);
            }
        }

        for (Binding getter : new ArrayList(desc.getters)) {
            for (String toName : getter.toNames) {
                Binding existing = byName.get(toName);
                if (existing == null) {
                    byName.put(toName, getter);
                    continue;
                }
                if (desc.fields.remove(existing)) {
                    continue;
                }
                if (existing.method != null && existing.method.getName().equals(getter.method.getName())) {
                    // inherited interface getter
                    desc.getters.remove(getter);
                    continue;
                }
                throw new JsonException("field encode to same name: " + toName);
            }
        }
    }

    private static ConstructorDescriptor getCtor(Class clazz) {
        ConstructorDescriptor cctor = new ConstructorDescriptor();
        if (JsoniterSpi.canCreate(clazz)) {
            cctor.objectFactory = JsoniterSpi.getObjectFactory(clazz);
            return cctor;
        }
        try {
            cctor.ctor = clazz.getDeclaredConstructor();
        } catch (Exception e) {
            cctor.ctor = null;
        }
        return cctor;
    }

    private static List getFields(Map lookup, ClassInfo classInfo, boolean includingPrivate) {
        ArrayList bindings = new ArrayList();
        for (Field field : getAllFields(classInfo.clazz, includingPrivate)) {
            if (Modifier.isStatic(field.getModifiers())) {
                continue;
            }
            if (Modifier.isTransient(field.getModifiers())) {
                continue;
            }
            if (!includingPrivate && !Modifier.isPublic(field.getType().getModifiers())) {
                continue;
            }
            if (includingPrivate) {
                field.setAccessible(true);
            }
            Binding binding = createBindingFromField(lookup, classInfo, field);
            bindings.add(binding);
        }
        return bindings;
    }

    private static Binding createBindingFromField(Map lookup, ClassInfo classInfo, Field field) {
        try {
            Binding binding = new Binding(classInfo, lookup, field.getGenericType());
            binding.fromNames = new String[]{field.getName()};
            binding.toNames = new String[]{field.getName()};
            binding.name = field.getName();
            binding.annotations = field.getAnnotations();
            binding.field = field;
            return binding;
        } catch (Exception e) {
            throw new JsonException("failed to create binding for field: " + field, e);
        }
    }

    private static List getAllFields(Class clazz, boolean includingPrivate) {
        List allFields = Arrays.asList(clazz.getFields());
        if (includingPrivate) {
            allFields = new ArrayList();
            Class current = clazz;
            while (current != null) {
                allFields.addAll(Arrays.asList(current.getDeclaredFields()));
                current = current.getSuperclass();
            }
        }
        return allFields;
    }

    private static List getSetters(Map lookup, ClassInfo classInfo, boolean includingPrivate) {
        ArrayList setters = new ArrayList();
        for (Method method : getAllMethods(classInfo.clazz, includingPrivate)) {
            if (Modifier.isStatic(method.getModifiers())) {
                continue;
            }
            String methodName = method.getName();
            if (methodName.length() < 4) {
                continue;
            }
            if (!methodName.startsWith("set")) {
                continue;
            }
            Type[] paramTypes = method.getGenericParameterTypes();
            if (paramTypes.length != 1) {
                continue;
            }
            if (!includingPrivate && !Modifier.isPublic(method.getParameterTypes()[0].getModifiers())) {
                continue;
            }
            if (includingPrivate) {
                method.setAccessible(true);
            }
            try {
                String fromName = translateSetterName(methodName);
                Binding binding = new Binding(classInfo, lookup, paramTypes[0]);
                binding.fromNames = new String[]{fromName};
                binding.name = fromName;
                binding.method = method;
                binding.annotations = method.getAnnotations();
                setters.add(binding);
            } catch (Exception e) {
                throw new JsonException("failed to create binding from setter: " + method, e);
            }
        }
        return setters;
    }

    private static List getAllMethods(Class clazz, boolean includingPrivate) {
        List allMethods = Arrays.asList(clazz.getMethods());
        if (includingPrivate) {
            allMethods = new ArrayList();
            Class current = clazz;
            while (current != null) {
                allMethods.addAll(Arrays.asList(current.getDeclaredMethods()));
                current = current.getSuperclass();
            }
        }
        return allMethods;
    }

    private static String translateSetterName(String methodName) {
        if (!methodName.startsWith("set")) {
            return null;
        }
        String fromName = methodName.substring("set".length());
        char[] fromNameChars = fromName.toCharArray();
        fromNameChars[0] = Character.toLowerCase(fromNameChars[0]);
        fromName = new String(fromNameChars);
        return fromName;
    }

    private static List getGetters(Map lookup, ClassInfo classInfo, boolean includingPrivate) {
        ArrayList getters = new ArrayList();
        for (Method method : getAllMethods(classInfo.clazz, includingPrivate)) {
            if (Modifier.isStatic(method.getModifiers())) {
                continue;
            }
            String methodName = method.getName();
            if ("getClass".equals(methodName)) {
                continue;
            }
            if (methodName.length() < 4) {
                continue;
            }
            if (!methodName.startsWith("get")) {
                continue;
            }
            if (method.getGenericParameterTypes().length != 0) {
                continue;
            }
            String toName = methodName.substring("get".length());
            char[] fromNameChars = toName.toCharArray();
            fromNameChars[0] = Character.toLowerCase(fromNameChars[0]);
            toName = new String(fromNameChars);
            Binding getter = new Binding(classInfo, lookup, method.getGenericReturnType());
            getter.toNames = new String[]{toName};
            getter.name = toName;
            getter.method = method;
            getter.annotations = method.getAnnotations();
            getters.add(getter);
        }
        return getters;
    }

    private static Map collectTypeVariableLookup(Type type) {
        HashMap vars = new HashMap();
        if (null == type) {
            return vars;
        }
        if (type instanceof ParameterizedType) {
            ParameterizedType pType = (ParameterizedType) type;
            Type[] actualTypeArguments = pType.getActualTypeArguments();
            Class clazz = (Class) pType.getRawType();
            for (int i = 0; i < clazz.getTypeParameters().length; i++) {
                TypeVariable variable = clazz.getTypeParameters()[i];
                vars.put(variable.getName() + "@" + clazz.getCanonicalName(), actualTypeArguments[i]);
            }
            vars.putAll(collectTypeVariableLookup(clazz.getGenericSuperclass()));
            return vars;
        }
        if (type instanceof Class) {
            Class clazz = (Class) type;
            vars.putAll(collectTypeVariableLookup(clazz.getGenericSuperclass()));
            return vars;
        }
        throw new JsonException("unexpected type: " + type);
    }

    public List allBindings() {
        ArrayList bindings = new ArrayList(8);
        bindings.addAll(fields);
        if (setters != null) {
            bindings.addAll(setters);
        }
        if (getters != null) {
            bindings.addAll(getters);
        }
        if (ctor != null) {
            bindings.addAll(ctor.parameters);
        }
        if (bindingTypeWrappers != null) {
            for (WrapperDescriptor setter : bindingTypeWrappers) {
                bindings.addAll(setter.parameters);
            }
        }
        return bindings;
    }

    public List allDecoderBindings() {
        ArrayList bindings = new ArrayList(8);
        bindings.addAll(fields);
        bindings.addAll(setters);
        if (ctor != null) {
            bindings.addAll(ctor.parameters);
        }
        for (WrapperDescriptor setter : bindingTypeWrappers) {
            bindings.addAll(setter.parameters);
        }
        return bindings;
    }

    public List allEncoderBindings() {
        ArrayList bindings = new ArrayList(8);
        bindings.addAll(fields);
        bindings.addAll(getters);
        return bindings;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy