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

pl.jalokim.utils.reflection.TypeWrapperBuilder Maven / Gradle / Ivy

There is a newer version: 4.0.3
Show newest version
package pl.jalokim.utils.reflection;

import lombok.Data;

import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import static java.util.Collections.singletonList;
import static pl.jalokim.utils.collection.Elements.elements;
import static pl.jalokim.utils.constants.Constants.EMPTY;
import static pl.jalokim.utils.constants.Constants.QUESTION_SIGN;
import static pl.jalokim.utils.reflection.ClassNameFixer.fixClassName;
import static pl.jalokim.utils.reflection.MetadataReflectionUtils.getClassForName;
import static pl.jalokim.utils.reflection.MetadataReflectionUtils.getParametrizedRawTypes;
import static pl.jalokim.utils.reflection.TypeMetadata.NATIVE_OBJECT_META;

/**
 * Class for build TypeMetadata from class, type, field.
 */
final class TypeWrapperBuilder {

    private static final CharsParser CHARS_PARSER = new CharsParser();

    private TypeWrapperBuilder() {

    }

    static TypeMetadata buildFromClass(Class someClass) {
        List genericsTypes = getParametrizedRawTypes(someClass);

        if (genericsTypes.isEmpty()) {
            return new TypeMetadata(someClass, null);
        }

        return new TypeMetadata(someClass, elements(genericsTypes)
                .map(type -> NATIVE_OBJECT_META)
                .asList());
    }

    static TypeMetadata buildFromField(Field field) {
        if (field.getType().isPrimitive()) {
            return buildFromClass(field.getType());
        } else if (field.getType().isArray()) {
            return buildForArrayField(field);
        }
        return buildFromType(field.getGenericType());
    }

    static TypeMetadata buildForArrayField(Field arrayField) {
        return buildFromType(arrayField.getGenericType());
    }

    static TypeMetadata buildFromType(Type type) {
        return buildFromType(type, null, null);
    }

    static TypeMetadata buildFromType(Type type,
                                      Field originalField,
                                      TypeMetadata currentContext) {
        String fullName = type.getTypeName();
        char[] arrayOfChars = fullName.toCharArray();
        InnerTypeMetaData current = new InnerTypeMetaData(originalField, currentContext);
        for (char nextChar : arrayOfChars) {
            current = CHARS_PARSER.parse(nextChar, current);
        }
        return buildFromInnerTypeMetaData(current);
    }

    static TypeMetadata buildFromInnerTypeMetaData(InnerTypeMetaData typeWrapper) {
        String typeName = typeWrapper.getClassName();
        if (QUESTION_SIGN.equals(typeName)) {
            return new TypeMetadata(Object.class, null);
        } else if (typeName.matches("^\\?extends(.)+")) {
            typeName = typeName.replace("?extends", EMPTY);
        } else if (typeName.matches("^\\?super(.)+")) {
            return new TypeMetadata(Object.class, null);
        }
        if (hasArraySignature(typeName)) {
            return buildFromArrayClass(typeWrapper, typeName);
        }

        Class realClass;
        try {
            realClass = getFixedClassName(typeName);
        } catch (ReflectionOperationException exception) {
            if (typeWrapper.getAvailableContext() != null) {
                Field originalField = typeWrapper.getOriginalField();
                Class fieldOwner = originalField.getDeclaringClass();
                TypeMetadata availableContext = typeWrapper.getAvailableContext();
                return availableContext.getTypeMetadataForField(fieldOwner, typeName);
            }
            throw new UnresolvedRealClassException(exception);
        }
        return new TypeMetadata(realClass,
                                buildGenericsList(typeWrapper.getGenericTypes()));
    }

    private static TypeMetadata buildFromArrayClass(InnerTypeMetaData typeWrapper,
                                                    String currentClassName) {
        Class rawClassForArray = getFixedClassName(currentClassName);
        String typeOfStoredInArray = currentClassName.replaceAll("(\\[])$", EMPTY);

        TypeMetadata genericDataOfArray;
        if (hasArraySignature(typeOfStoredInArray)) {
            genericDataOfArray = buildFromArrayClass(typeWrapper, typeOfStoredInArray);
        } else {
            genericDataOfArray = new TypeMetadata(getFixedClassName(typeOfStoredInArray),
                                                  buildGenericsList(typeWrapper.getGenericTypes()));
        }
        return new TypeMetadata(rawClassForArray, singletonList(genericDataOfArray));
    }

    private static boolean hasArraySignature(String className) {
        return className.matches(".*\\[]");
    }

    static List buildGenericsList(List generics) {
        return elements(generics)
                .map(TypeWrapperBuilder::buildFromInnerTypeMetaData)
                .filter(Objects::nonNull)
                .asList();
    }

    private static Class getFixedClassName(String className) {
        return getClassForName(fixClassName(className));
    }

    @Data
    static class InnerTypeMetaData {
        private InnerTypeMetaData parent;
        @SuppressWarnings("PMD.AvoidStringBufferField")
        private final StringBuilder classNameBuilder = new StringBuilder();
        private final List genericTypes = new ArrayList<>();
        private final Field originalField;
        private final TypeMetadata availableContext;

        void addChild(InnerTypeMetaData innerTypeMetaData) {
            genericTypes.add(innerTypeMetaData);
        }

        void appendToClassName(char nextChar) {
            classNameBuilder.append(nextChar);
        }

        String getClassName() {
            return classNameBuilder.toString();
        }

        @Override
        public String toString() {
            String parentText = parent == null ? "null" : parent.getClassName();
            return "InnerTypeMetaData{"
                   + "parent=" + parentText
                   + ", className=" + getClassName()
                   + ", genericTypes=" + genericTypes
                   + '}';
        }
    }

    private static class CharsParser {

        private static final char START_GENERIC_CHAR = '<';
        private static final char COMMA_CHAR = ',';
        private static final char END_GENERIC_CHAR = '>';
        private static final char SPACE_CHAR = ' ';

        InnerTypeMetaData parse(char nextChar, InnerTypeMetaData currentMetadata) {
            if (nextChar == START_GENERIC_CHAR) {
                InnerTypeMetaData child = new InnerTypeMetaData(currentMetadata.getOriginalField(),
                                                                currentMetadata.getAvailableContext());
                currentMetadata.addChild(child);
                child.setParent(currentMetadata);
                return child;
            } else if (nextChar == COMMA_CHAR) {
                InnerTypeMetaData parent = currentMetadata.getParent();
                InnerTypeMetaData child = new InnerTypeMetaData(currentMetadata.getOriginalField(),
                                                                currentMetadata.getAvailableContext());
                parent.addChild(child);
                child.setParent(parent);
                return child;
            } else if (nextChar == END_GENERIC_CHAR) {
                return currentMetadata.getParent();
            } else if (nextChar == SPACE_CHAR) {
                return currentMetadata;
            } else {
                currentMetadata.appendToClassName(nextChar);
                return currentMetadata;
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy