Java tutorial
/* * 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.runtime.base.internal.registry; import com.google.common.collect.Lists; import org.apache.commons.lang.StringUtils; import org.gradle.api.Action; import org.gradle.api.internal.project.ProjectIdentifier; import org.gradle.api.plugins.ExtensionContainer; import org.gradle.language.base.plugins.ComponentModelBasePlugin; import org.gradle.model.InvalidModelRuleDeclarationException; import org.gradle.model.internal.core.Inputs; import org.gradle.model.internal.core.ModelMutator; import org.gradle.model.internal.core.ModelReference; import org.gradle.model.internal.core.ModelType; import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor; import org.gradle.model.internal.inspect.MethodRuleDefinition; import org.gradle.model.internal.inspect.MethodRuleDefinitionHandler; import org.gradle.model.internal.inspect.RuleSourceDependencies; import org.gradle.model.internal.registry.ModelRegistry; import org.gradle.runtime.base.InvalidComponentModelException; import org.gradle.runtime.base.TypeBuilder; import java.util.List; // TODO:DAZ Convert to use ModelType throughout public abstract class AbstractAnnotationModelRuleDefinitionHandler<T, U> implements MethodRuleDefinitionHandler { protected String modelName; private final Class annotationClass; private ModelType<T> baseInterface; private ModelType<U> baseImplementation; private ModelType<? extends TypeBuilder> builderInterface; public AbstractAnnotationModelRuleDefinitionHandler(String modelName, Class annotationClass, Class<T> baseInterface, Class<U> baseImplementation, Class<? extends TypeBuilder> builderInterface) { this.modelName = modelName; this.annotationClass = annotationClass; this.baseInterface = ModelType.of(baseInterface); this.baseImplementation = ModelType.of(baseImplementation); this.builderInterface = ModelType.of(builderInterface); } public boolean isSatisfiedBy(MethodRuleDefinition element) { return element.getAnnotation(annotationClass) != null; } public String getDescription() { return String.format("annotated with @%s", annotationClass.getSimpleName()); } abstract protected Action<MutationActionParameter> createMutationAction(Class<? extends T> type, Class<? extends U> implementation); abstract protected TypeBuilderInternal createBuilder(); public void register(MethodRuleDefinition ruleDefinition, ModelRegistry modelRegistry, RuleSourceDependencies dependencies) { try { Class<? extends T> type = readType(ruleDefinition); Class<? extends U> implementation = determineImplementationType(ruleDefinition, type); dependencies.add(ComponentModelBasePlugin.class); if (implementation != null) { modelRegistry.mutate(new RegisterTypeRule(ruleDefinition.getDescriptor(), createMutationAction(type, implementation))); } } catch (InvalidComponentModelException e) { invalidModelRule(ruleDefinition, e); } } protected Class<? extends T> readType(MethodRuleDefinition ruleDefinition) { if (!ModelType.of(Void.TYPE).equals(ruleDefinition.getReturnType())) { throw new InvalidComponentModelException( String.format("%s method must not have a return value.", annotationClass.getSimpleName())); } if (ruleDefinition.getReferences().size() != 1) { throw new InvalidComponentModelException( String.format("%s method must have a single parameter of type '%s'.", annotationClass.getSimpleName(), builderInterface.toString())); } ModelType<?> builder = ruleDefinition.getReferences().get(0).getType(); if (!builderInterface.isAssignableFrom(builder)) { throw new InvalidComponentModelException( String.format("%s method must have a single parameter of type '%s'.", annotationClass.getSimpleName(), builderInterface.toString())); } if (builder.getTypeVariables().size() != 1) { throw new InvalidComponentModelException(String .format("Parameter of type '%s' must declare a type parameter.", builderInterface.toString())); } ModelType<?> subType = builder.getTypeVariables().get(0); if (!baseInterface.isAssignableFrom(subType) || subType.isAssignableFrom(baseInterface)) { throw new InvalidComponentModelException( String.format("%s type '%s' is not a concrete subtype of '%s'.", StringUtils.capitalize(modelName), subType.toString(), baseInterface.toString())); } // TODO:DAZ Propogate ModelType out return (Class<? extends T>) subType.getRawClass(); } protected void invalidModelRule(MethodRuleDefinition ruleDefinition, InvalidComponentModelException e) { StringBuilder sb = new StringBuilder(); ruleDefinition.getDescriptor().describeTo(sb); sb.append(String.format(" is not a valid %s model rule method.", modelName)); throw new InvalidModelRuleDeclarationException(sb.toString(), e); } protected Class<? extends U> determineImplementationType(MethodRuleDefinition ruleDefinition, Class<? extends T> typeClass) { ModelType<? extends T> type = ModelType.of(typeClass); TypeBuilderInternal builder = createBuilder(); ruleDefinition.getRuleInvoker().invoke(builder); Class<?> implementation = builder.getDefaultImplementation(); if (implementation == null) { return null; } ModelType<?> implementationType = ModelType.of(implementation); if (!baseImplementation.isAssignableFrom(implementationType)) { throw new InvalidComponentModelException( String.format("%s implementation '%s' must extend '%s'.", StringUtils.capitalize(modelName), implementationType.toString(), baseImplementation.toString())); } if (!type.isAssignableFrom(implementationType)) { throw new InvalidComponentModelException(String.format("%s implementation '%s' must implement '%s'.", StringUtils.capitalize(modelName), implementationType.toString(), type.toString())); } try { implementation.getConstructor(); } catch (NoSuchMethodException nsmException) { throw new InvalidComponentModelException( String.format("%s implementation '%s' must have public default constructor.", StringUtils.capitalize(modelName), implementationType.toString())); } // TODO:DAZ Propogate ModelType out return (Class<? extends U>) implementationType.getRawClass(); } protected static class RegisterTypeRule implements ModelMutator<ExtensionContainer> { private final ModelRuleDescriptor descriptor; private final ModelReference<ExtensionContainer> subject; private final List<ModelReference<?>> inputs = Lists.newArrayList(); private final Action<MutationActionParameter> mutationAction; protected RegisterTypeRule(ModelRuleDescriptor descriptor, Action<MutationActionParameter> mutationAction) { this.descriptor = descriptor; this.mutationAction = mutationAction; subject = ModelReference.of("extensions", ExtensionContainer.class); final ModelReference<?> input = ModelReference.of(ProjectIdentifier.class); inputs.add(input); } public ModelReference<ExtensionContainer> getSubject() { return subject; } public List<ModelReference<?>> getInputs() { return inputs; } public void mutate(ExtensionContainer extensions, Inputs inputs) { mutationAction.execute(new MutationActionParameter(extensions, inputs)); } public ModelRuleDescriptor getDescriptor() { return descriptor; } } protected static class MutationActionParameter { final ExtensionContainer extensions; final Inputs inputs; public MutationActionParameter(ExtensionContainer extensions, Inputs inputs) { this.extensions = extensions; this.inputs = inputs; } } }