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

io.sundr.builder.internal.utils.BuilderUtils Maven / Gradle / Ivy

/*
 * Copyright 2015 The original authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package io.sundr.builder.internal.utils;


import io.sundr.SundrException;
import io.sundr.builder.Constants;
import io.sundr.builder.annotations.*;
import io.sundr.builder.internal.BuildableRepository;
import io.sundr.builder.internal.BuilderContext;
import io.sundr.builder.internal.BuilderContextManager;
import io.sundr.builder.internal.functions.CollectionTypes;
import io.sundr.builder.internal.functions.TypeAs;
import io.sundr.codegen.DefinitionRepository;
import io.sundr.codegen.functions.ClassTo;
import io.sundr.codegen.functions.ElementTo;
import io.sundr.codegen.model.ClassRef;
import io.sundr.codegen.model.Method;
import io.sundr.codegen.model.PrimitiveRef;
import io.sundr.codegen.model.Property;
import io.sundr.codegen.model.TypeDef;
import io.sundr.codegen.model.TypeParamDef;
import io.sundr.codegen.model.TypeParamDefBuilder;
import io.sundr.codegen.model.TypeParamRef;
import io.sundr.codegen.model.TypeRef;
import io.sundr.codegen.utils.TypeUtils;

import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.MirroredTypeException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import static io.sundr.builder.internal.functions.TypeAs.UNWRAP_ARRAY_OF;
import static io.sundr.builder.internal.functions.TypeAs.UNWRAP_COLLECTION_OF;
import static io.sundr.codegen.utils.StringUtils.captializeFirst;

public class BuilderUtils {

    private BuilderUtils() {}

    public static boolean isAbstract(TypeRef  typeRef) {
        DefinitionRepository repository =  BuilderContextManager.getContext().getDefinitionRepository();
        TypeDef def = repository.getDefinition(typeRef);
        if (def == null && typeRef instanceof ClassRef) {
            def = ((ClassRef)typeRef).getDefinition();
        }
        return def != null ? def.isAbstract() : false;
    }

    public static boolean isBuildable(TypeRef  typeRef) {
        BuildableRepository repository =  BuilderContextManager.getContext().getBuildableRepository();
        return repository.isBuildable(typeRef);
    }

    public static boolean isBuildable(TypeDef  typeDef) {
        BuildableRepository repository =  BuilderContextManager.getContext().getBuildableRepository();
        return repository.isBuildable(typeDef);
    }

    public static ClassRef findBuildableSuperClassRef(TypeDef clazz) {
        BuildableRepository repository =  BuilderContextManager.getContext().getBuildableRepository();

        for (ClassRef superClass : clazz.getExtendsList()) {
            if (repository.isBuildable(superClass)) {
                return superClass;
            }
        }
        return null;
    }

    public static TypeDef findBuildableSuperClass(TypeDef clazz) {
        BuildableRepository repository =  BuilderContextManager.getContext().getBuildableRepository();

        for (ClassRef superClass : clazz.getExtendsList()) {
            if (repository.isBuildable(superClass)) {
                return repository.getBuildable(superClass);
            }
        }
        return null;
    }

    public static Method findBuildableConstructor(TypeDef clazz) {
        //1st pass go for annotated method
        for (Method candidate : clazz.getConstructors()) {
            if (candidate.getAnnotations().contains(Constants.BUILDABLE_ANNOTATION_REF)) {
                return candidate;
            }
        }

        //2nd pass go for the first non-empty constructor
        for (Method candidate : clazz.getConstructors()) {
            if (candidate.getArguments().size() != 0) {
                return candidate;
            }
        }

        if (!clazz.getConstructors().isEmpty()) {
            return clazz.getConstructors().iterator().next();
        } else {
            throw new IllegalStateException("Could not find buildable constructor in: ["+clazz.getFullyQualifiedName()+"].");
        }

    }

    public static Method findGetter(TypeDef clazz, Property property) {
        TypeDef current = clazz;
        while (current!= null && !current.equals(TypeDef.OBJECT)) {
            for (Method method : current.getMethods()) {
                if (isApplicableGetterOf(method, property)) {
                    return method;
                }
            }
            if (!current.getExtendsList().iterator().hasNext()) {
                break;
            }
            String fqn = current.getExtendsList().iterator().next().getDefinition().getFullyQualifiedName();
            current = DefinitionRepository.getRepository().getDefinition(fqn);
        }
        throw new SundrException("No getter found for property: " + property.getName() + " on class: " + clazz.getFullyQualifiedName());
    }

    public static boolean hasSetter(TypeDef clazz, Property property) {
        for (Method method : clazz.getMethods()) {
            if (isApplicableSetterOf(method, property)) {
                return true;
            }
        }
        return false;
    }


    public static boolean hasOrInheritsSetter(TypeDef clazz, Property property) {
        TypeDef current = clazz;
        //Iterate parent objects and check for properties with setters but not ctor arguments.
        while (current!= null && !current.equals(TypeDef.OBJECT)) {
            for (Method method : current.getMethods()) {
                if (isApplicableSetterOf(method, property)) {
                    return true;
                }
            }

            if (!current.getExtendsList().isEmpty()) {
                String fqn = current.getExtendsList().iterator().next().getDefinition().getFullyQualifiedName();
                current = DefinitionRepository.getRepository().getDefinition(fqn);
            } else {
                current = null;
            }
        }
        return false;
    }

    private static boolean isApplicableGetterOf(Method method, Property property) {
        if (!method.getReturnType().isAssignableFrom(property.getTypeRef())) {
            return false;
        }

        if (method.getName().endsWith("get" + property.getNameCapitalized())) {
            return true;
        }

        if (method.getName().endsWith("is" + property.getNameCapitalized())) {
            return true;
        }
        return false;
    }


    private static boolean isApplicableSetterOf(Method method, Property property) {
        if (method.getArguments().size() != 1) {
            return false;
        } else if (!method.getArguments().get(0).getTypeRef().equals(property.getTypeRef())) {
            return false;
        } else if (method.getName().endsWith("set" + property.getNameCapitalized())) {
            return true;
        }
        return false;
    }


    /**
     * Checks if method has a specific argument.
     * @param method        The method.
     * @param property      The arguement.
     * @return              True if matching argument if found.
     */
    public static boolean methodHasArgument(Method method, Property property) {
        for (Property candidate : method.getArguments()) {
            if (candidate.equals(property)) {
                return true;
            }
        }
        return false;
    }

    public static boolean hasBuildableConstructorWithArgument(TypeDef clazz, Property property) {
        Method constructor = findBuildableConstructor(clazz);
        if (constructor == null) {
            return false;
        } else {
            return methodHasArgument(constructor, property);
        }

    }

    /**
     * Checks if there is a default constructor available.
     *
     * @param item The clazz to check.
     * @return
     */
    public static boolean hasDefaultConstructor(TypeDef item) {
        if (item == null) {
            return false;
        } else if (item.getConstructors().isEmpty()) {
            return true;
        } else {
            for (Method constructor : item.getConstructors()) {
                if (constructor.getArguments().size() == 0) {
                    return true;
                }
            }
        }
        return false;
    }


    public static Set getInlineableConstructors(Property property) {
        Set result = new HashSet();
        TypeRef unwrapped = TypeAs.combine(TypeAs.UNWRAP_COLLECTION_OF, TypeAs.UNWRAP_ARRAY_OF).apply(property.getTypeRef());

        if (unwrapped instanceof ClassRef) {
            for (Method candidate : ((ClassRef)unwrapped).getDefinition().getConstructors()) {
                if (isInlineable(candidate)) {
                    result.add(candidate);
                }
            }
        }

        //We try both types to make sure...
        TypeDef fromRepo = BuilderContextManager.getContext().getDefinitionRepository().getDefinition(unwrapped);
        if (fromRepo != null) {
            for (Method candidate : fromRepo.getConstructors()) {
                if (isInlineable(candidate)) {
                    result.add(candidate);
                }
            }
        }
        return result;
    }

    public static boolean isInlineable(Method method) {
        if (method.getArguments().size() == 0 || method.getArguments().size() > 5) {
            return false;
        }

        for (Property argument : method.getArguments()) {
            if (!(argument.getTypeRef() instanceof ClassRef)) {
                continue;
            } else if (((ClassRef)argument.getTypeRef()).getDefinition().getFullyQualifiedName().startsWith("java.lang")) {
                continue;
            } else {
                return false;
            }
        }
        return true;
    }

    public static TypeDef getInlineType(BuilderContext context, Inline inline) {
        try {
            return ClassTo.TYPEDEF.apply(inline.type());
        } catch (MirroredTypeException e) {
            Element element = context.getTypes().asElement(e.getTypeMirror());
            return ElementTo.TYPEDEF.apply((TypeElement) element);
        }
    }

    public static TypeDef getInlineReturnType(BuilderContext context, Inline inline, TypeDef fallback) {
        try {
            Class returnType = inline.returnType();
            if (returnType == null) {
                return fallback;
            }
            return ClassTo.TYPEDEF.apply(inline.returnType());
        } catch (MirroredTypeException e) {
            if (None.FQN.equals(e.getTypeMirror().toString())) {
                return fallback;
            }

            Element element = context.getTypes().asElement(e.getTypeMirror());
            return ElementTo.TYPEDEF.apply((TypeElement) element);
        }
    }

    public static Set getBuildableReferences(BuilderContext context, Buildable buildable) {
        Set result = new LinkedHashSet();
        for (BuildableReference ref : buildable.refs()) {
            try {
                result.add(context.getElements().getTypeElement(ref.value().getCanonicalName()));
            } catch (MirroredTypeException e) {
                result.add(context.getElements().getTypeElement(e.getTypeMirror().toString()));
            }
        }
        return result;
    }

    public static Set getBuildableReferences(BuilderContext context, ExternalBuildables buildable) {
        Set result = new LinkedHashSet();
        for (BuildableReference ref : buildable.refs()) {
            try {
                result.add(context.getElements().getTypeElement(ref.value().getCanonicalName()));
            } catch (MirroredTypeException e) {
                result.add(context.getElements().getTypeElement(e.getTypeMirror().toString()));
            }
        }
        return result;
    }

    public static boolean isPrimitive(TypeRef type) {
        return type instanceof PrimitiveRef;
    }

    public static boolean isMap(TypeRef type) {
       return CollectionTypes.IS_MAP.apply(type);
    }

    public static boolean isList(TypeRef type) {
        return CollectionTypes.IS_LIST.apply(type);
    }

    public static boolean isSet(TypeRef type) {
        return CollectionTypes.IS_SET.apply(type);
    }

    public static boolean isCollection(TypeRef type) {
        return CollectionTypes.IS_COLLECTION.apply(type);
    }

    public static boolean isBoolean(TypeRef type) {
        if (type instanceof PrimitiveRef) {
            return ((PrimitiveRef)type).getName().equals("boolean");
        } else if (!(type instanceof ClassRef)) {
            return false;
        } else {
            return ((ClassRef)type).getDefinition().equals(Constants.BOOLEAN);
        }
    }


    public static boolean isArray(TypeRef type) {

        if (type instanceof ClassRef) {
            return ((ClassRef)type).getDimensions() > 0;
        } else if (type instanceof PrimitiveRef) {
            return ((PrimitiveRef)type).getDimensions() > 0;
        } else if (type instanceof TypeParamRef) {
            return ((TypeParamRef)type).getDimensions() > 0;
        } else {
            return false;
        }
    }

    public static TypeParamDef getNextGeneric(TypeDef type, TypeParamDef... excluded) {
        return getNextGeneric(type, Arrays.asList(excluded));
    }


    public static TypeParamDef getNextGeneric(TypeDef type, Collection excluded) {
        Set skip = new HashSet();
        for (String s : allGenericsOf(type)) {
            skip.add(s);
        }

        for (TypeParamDef e : excluded) {
            skip.add(e.getName());
        }

        for (int i = 0; i < 10; i++) {
            for (int j = 0; j < GENERIC_NAMES.length; j++) {

                String name = GENERIC_NAMES[j] + ((i > 0) ? String.valueOf(i) : "");
                if (!skip.contains(name)) {
                    return new TypeParamDefBuilder().withName(name).build();
                }
            }
        }
        throw new IllegalStateException("Could not allocate generic parameter letter for: " + type.getFullyQualifiedName());
    }

    public static Set allGenericsOf(TypeDef clazz) {
        Set result = new HashSet();

        for (TypeParamDef paramDef : clazz.getParameters()) {
            result.add(paramDef.getName());
        }
        for (Property property : clazz.getProperties()) {
            result.addAll(allGenericsOf(property));
        }

        for (Method method : clazz.getMethods()) {
            result.addAll(allGenericsOf(method));
        }

        return result;
    }

    public static Set allGenericsOf(TypeRef type) {
        Set result = new HashSet();
        if (type instanceof ClassRef) {
            for (TypeRef ref : ((ClassRef)type).getArguments()) {
                if (ref instanceof TypeParamRef) {
                    result.add(((TypeParamRef)ref).getName());
                }
            }
        }
        return result;
    }


    public static Collection allGenericsOf(Property property) {
        return allGenericsOf(property.getTypeRef());
    }

    public static Collection allGenericsOf(Method method) {
        Set result = new HashSet(allGenericsOf(method.getReturnType()));
        for (Property property : method.getArguments()) {
            result.addAll(allGenericsOf(property));

        }
        return result;
    }

    private static final String[] GENERIC_NAMES = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S"};

    public static String fullyQualifiedNameDiff(Property property) {
        TypeRef typeRef = property.getTypeRef();
        Map map = DefinitionRepository.getRepository().getReferenceMap();
        if (typeRef instanceof ClassRef) {
            ClassRef classRef = (ClassRef)  TypeAs.combine(UNWRAP_COLLECTION_OF, UNWRAP_ARRAY_OF).apply(typeRef);
            String fqn = map.get(classRef.getDefinition().getName());
            if (!classRef.getDefinition().getFullyQualifiedName().equals(fqn)) {
                return captializeFirst(TypeUtils.fullyQualifiedNameDiff(fqn, classRef.getFullyQualifiedName()));
            }
        }
        return "";
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy