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

com.fluxtion.compiler.generation.model.ConstructorMatcherPredicate Maven / Gradle / Ivy

/*
 * Copyright (C) 2021 2024 gregory higgins.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the Server Side Public License, version 1,
 * as published by MongoDB, Inc.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * Server Side License for more details.
 *
 * You should have received a copy of the Server Side Public License
 * along with this program.  If not, see
 * .
 */
package com.fluxtion.compiler.generation.model;

import com.fluxtion.compiler.generation.model.Field.MappedField;
import com.fluxtion.runtime.annotations.builder.AssignToField;
import com.google.common.base.Predicate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Constructor;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author 2024 gregory higgins.
 */
@SuppressWarnings("rawtypes")
class ConstructorMatcherPredicate implements Predicate {

    private final Logger LOGGER = LoggerFactory.getLogger(ConstructorMatcherPredicate.class);
    private final Field.MappedField[] cstrArgList;
    private final HashSet privateFields;
    private final boolean nameAndType;

    public static Predicate matchConstructorNameAndType(Field.MappedField[] cstrArgList, HashSet privateFields) {
        return new ConstructorMatcherPredicate(cstrArgList, privateFields);
    }

    public static Predicate matchConstructorType(Field.MappedField[] cstrArgList, HashSet privateFields) {
        return new ConstructorMatcherPredicate(cstrArgList, privateFields, false);
    }

    public ConstructorMatcherPredicate(Field.MappedField[] cstrArgList, HashSet privateFields) {
        this.cstrArgList = cstrArgList;
        this.privateFields = privateFields;
        this.nameAndType = true;
    }

    public ConstructorMatcherPredicate(Field.MappedField[] cstrArgList, HashSet privateFields, boolean nameAndType) {
        this.cstrArgList = cstrArgList;
        this.privateFields = privateFields;
        this.nameAndType = nameAndType;
    }

    @Override
    public boolean apply(Constructor input) {
        boolean match = cstrArgList[0] != null;
        if (match) {
            LOGGER.debug("already matched constructor, ignoring");
            return false;
        } else {
            LOGGER.debug("unmatched constructor, reset constructorArgs");
            Arrays.fill(cstrArgList, null);
        }
        Parameter[] parameters = input.getParameters();
        int parameterCount = parameters.length;
        if (parameterCount == 0 || parameterCount != privateFields.size()) {
            LOGGER.debug("parameterCount:{} privateFieldsCount:{} mismatch reject constructor", parameterCount, privateFields.size());
        } else {
            //possible match
            int matchCount = 0;
            for (Field.MappedField mappedInstance : privateFields) {
                String varName = mappedInstance.mappedName;
                Class parentClass = mappedInstance.parentClass();
                Class realClass = mappedInstance.realClass();
                LOGGER.debug("match field var:{}, type:{}", varName, parentClass);
                //                            Class parentClass = mappedInstance.collection?List.class:parentInstance.getClass();
                boolean matchOnName = false;
                LOGGER.debug("matching constructor by type and name");
                //match array
                for (int i = 0; i < parameters.length; i++) {
                    if (parameters[i] == null) {
                        continue;
                    }
                    Parameter parameter = parameters[i];
                    String paramName = parameter.getName();
                    if (parameter.getAnnotation(AssignToField.class) != null) {
                        paramName = parameter.getAnnotation(AssignToField.class).value();
                        LOGGER.debug("assigning parameter name from annotation AssignToField " +
                                "fieldName:'{}' overriding:'{}'", paramName, parameter.getName());
                    }
                    Class parameterType = parameters[i].getType();
                    LOGGER.debug("constructor parameter type:{}, paramName:{}, varName:{}", parameterType, paramName, varName);
                    if (parameterType != null
                            && (parameterType.isAssignableFrom(parentClass) || parameterType.isAssignableFrom(realClass))
                            && paramName.equals(varName)) {
                        matchCount++;
                        parameters[i] = null;
                        cstrArgList[i] = mappedInstance;
                        matchOnName = true;
                        LOGGER.debug("matched constructor arg:{}, by type and name", paramName);
                        break;
                    }
                }
                if (!matchOnName && !nameAndType) {
                    LOGGER.debug("no match, matching constructor by type only");
                    for (int i = 0; i < parameters.length; i++) {
                        if (parameters[i] == null) {
                            continue;
                        }
                        Class parameterType = parameters[i].getType();
                        String paramName = parameters[i].getName();
                        LOGGER.debug("constructor parameter type:{}, paramName:{}, varName:{}", parameterType, paramName, varName);
                        if (parameterType != null && (parameterType.isAssignableFrom(parentClass) || parameterType.isAssignableFrom(realClass))) {
                            matchCount++;
                            parameters[i] = null;
                            cstrArgList[i] = mappedInstance;
                            matchOnName = true;
                            LOGGER.debug("matched constructor arg:{}, by type only", paramName);
                            break;
                        }
                    }
                    if (!matchOnName) {
                        LOGGER.debug("no match for varName:{}", varName);
                        break;
                    }
                }
            }
            if (matchCount == parameterCount) {
                LOGGER.debug("matched constructor:{}", input);
                match = true;
            } else {
                LOGGER.debug("unmatched constructor:{}", input);
                Arrays.fill(cstrArgList, null);
            }
        }
        return match;
    }

    public static List validateNoTypeClash(Set privateFields, Constructor constructor) {
        Set mappedNames = Arrays.stream(constructor.getParameters())
                .filter(p -> p.getAnnotation(AssignToField.class) != null)
                .map(p -> p.getAnnotation(AssignToField.class))
                .map(AssignToField::value)
                .collect(Collectors.toSet());

        Set filteredFields = privateFields.stream()
                .filter(m -> !mappedNames.contains(m.mappedName))
                .collect(Collectors.toSet());

        List output = filteredFields.stream()
                .filter(m -> {
                    Class classToTest = m.parentClass();
                    HashSet setToTest = new HashSet<>(filteredFields);
                    setToTest.remove(m);
                    return setToTest.stream()
                            .map(MappedField::parentClass)
                            .anyMatch(c -> {
                                boolean val = c.isAssignableFrom(classToTest) || classToTest.isAssignableFrom(c);
                                return val;
                            });
                })
                .map(MappedField::getMappedName)
                .collect(Collectors.toList());
        return output;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy