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

org.gradle.internal.rules.RuleSourceBackedRuleAction Maven / Gradle / Ivy

There is a newer version: 8.6
Show newest version
/*
 * Copyright 2014 the original author or 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 org.gradle.internal.rules;

import org.gradle.api.specs.Spec;
import org.gradle.internal.reflect.JavaMethod;
import org.gradle.model.Mutate;
import org.gradle.model.internal.inspect.DefaultRuleSourceValidationProblemCollector;
import org.gradle.model.internal.inspect.FormattingValidationProblemCollector;
import org.gradle.model.internal.inspect.RuleSourceValidationProblemCollector;
import org.gradle.model.internal.type.ModelType;
import org.gradle.util.CollectionUtils;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

public class RuleSourceBackedRuleAction implements RuleAction {
    private final R instance;
    private final JavaMethod ruleMethod;
    private final List> inputTypes;

    private RuleSourceBackedRuleAction(R instance, JavaMethod ruleMethod) {
        this.instance = instance;
        this.ruleMethod = ruleMethod;
        this.inputTypes = determineInputTypes(ruleMethod.getParameterTypes());
    }

    public static  RuleSourceBackedRuleAction create(ModelType subjectType, R ruleSourceInstance) {
        ModelType ruleSourceType = ModelType.typeOf(ruleSourceInstance);
        List mutateMethods = findAllMethods(ruleSourceType.getConcreteClass(), new Spec() {
            @Override
            public boolean isSatisfiedBy(Method element) {
                return element.isAnnotationPresent(Mutate.class);
            }
        });
        FormattingValidationProblemCollector problemsFormatter = new FormattingValidationProblemCollector("rule source", ruleSourceType);
        RuleSourceValidationProblemCollector problems = new DefaultRuleSourceValidationProblemCollector(problemsFormatter);

        if (mutateMethods.size() == 0) {
            problems.add("Must have at exactly one method annotated with @" + Mutate.class.getName());
        } else {
            if (mutateMethods.size() > 1) {
                problems.add("More than one method is annotated with @" + Mutate.class.getName());
            }

            for (Method ruleMethod : mutateMethods) {
                if (ruleMethod.getReturnType() != Void.TYPE) {
                    problems.add(ruleMethod, "A rule method must return void");
                }
                Type[] parameterTypes = ruleMethod.getGenericParameterTypes();
                if (parameterTypes.length == 0 || !subjectType.isAssignableFrom(ModelType.of(parameterTypes[0]))) {
                    problems.add(ruleMethod, String.format("First parameter of a rule method must be of type %s", subjectType));
                }
            }
        }

        if (problemsFormatter.hasProblems()) {
            throw new RuleActionValidationException(problemsFormatter.format());
        }

        return new RuleSourceBackedRuleAction(ruleSourceInstance, new JavaMethod(subjectType.getConcreteClass(), mutateMethods.get(0)));
    }

    public static List> determineInputTypes(Class[] parameterTypes) {
        return Arrays.asList(parameterTypes).subList(1, parameterTypes.length);
    }

    @Override
    public List> getInputTypes() {
        return inputTypes;
    }

    @Override
    public void execute(T subject, List inputs) {
        Object[] args = new Object[inputs.size() + 1];
        args[0] = subject;
        for (int i = 0; i < inputs.size(); i++) {
            Object input =  inputs.get(i);
            args[i+1] = input;
        }
        ruleMethod.invoke(instance, args);
    }

    private static List findAllMethods(Class target, Spec predicate) {
        return findAllMethodsInternal(target, predicate, new MultiMap(), new ArrayList(), false);
    }

    private static class MultiMap extends HashMap> {
        @Override
        public List get(Object key) {
            if (!containsKey(key)) {
                @SuppressWarnings("unchecked") K keyCast = (K) key;
                put(keyCast, new LinkedList());
            }

            return super.get(key);
        }
    }

    private static List findAllMethodsInternal(Class target, Spec predicate, MultiMap seen, List collector, boolean stopAtFirst) {
        for (final Method method : target.getDeclaredMethods()) {
            List seenWithName = seen.get(method.getName());
            Method override = CollectionUtils.findFirst(seenWithName, new Spec() {
                @Override
                public boolean isSatisfiedBy(Method potentionOverride) {
                    return potentionOverride.getName().equals(method.getName())
                        && Arrays.equals(potentionOverride.getParameterTypes(), method.getParameterTypes());
                }
            });


            if (override == null) {
                seenWithName.add(method);
                if (predicate.isSatisfiedBy(method)) {
                    collector.add(method);
                    if (stopAtFirst) {
                        return collector;
                    }
                }
            }
        }

        Class parent = target.getSuperclass();
        if (parent != null) {
            return findAllMethodsInternal(parent, predicate, seen, collector, stopAtFirst);
        }

        return collector;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy