/* * Copyright 2013 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.api.internal.artifacts.dsl; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import groovy.lang.Closure; import org.gradle.api.Action; import org.gradle.api.InvalidUserCodeException; import org.gradle.api.artifacts.ComponentMetadataDetails; import org.gradle.api.artifacts.ModuleIdentifier; import org.gradle.api.artifacts.dsl.ComponentMetadataHandler; import org.gradle.api.artifacts.ivy.IvyModuleDescriptor; import org.gradle.api.internal.artifacts.ComponentMetadataProcessor; import org.gradle.api.internal.artifacts.ImmutableModuleIdentifierFactory; import org.gradle.api.internal.artifacts.ivyservice.DefaultIvyModuleDescriptor; import org.gradle.api.internal.artifacts.repositories.resolver.ComponentMetadataDetailsAdapter; import org.gradle.api.internal.notations.ModuleIdentifierNotationConverter; import org.gradle.api.specs.Spec; import org.gradle.api.specs.Specs; import org.gradle.internal.component.external.model.IvyModuleResolveMetadata; import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata; import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetadata; import org.gradle.internal.reflect.Instantiator; import org.gradle.internal.resolve.ModuleVersionResolveException; import org.gradle.internal.rules.DefaultRuleActionAdapter; import org.gradle.internal.rules.DefaultRuleActionValidator; import org.gradle.internal.rules.RuleAction; import org.gradle.internal.rules.RuleActionAdapter; import org.gradle.internal.rules.RuleActionValidator; import org.gradle.internal.rules.SpecRuleAction; import org.gradle.internal.typeconversion.NotationParser; import org.gradle.internal.typeconversion.NotationParserBuilder; import org.gradle.internal.typeconversion.UnsupportedNotationException; import java.util.Collections; import java.util.List; import java.util.Set; public class DefaultComponentMetadataHandler implements ComponentMetadataHandler, ComponentMetadataProcessor { private static final String ADAPTER_NAME = ComponentMetadataHandler.class.getSimpleName(); private static final List<Class<?>> VALIDATOR_PARAM_LIST = Collections.<Class<?>>singletonList(IvyModuleDescriptor.class); private static final String INVALID_SPEC_ERROR = "Could not add a component metadata rule for module '%s'."; private final Instantiator instantiator; private final Set<SpecRuleAction<? super ComponentMetadataDetails>> rules = Sets.newLinkedHashSet(); private final RuleActionAdapter<ComponentMetadataDetails> ruleActionAdapter; private final NotationParser<Object, ModuleIdentifier> moduleIdentifierNotationParser; public DefaultComponentMetadataHandler(Instantiator instantiator, RuleActionAdapter<ComponentMetadataDetails> ruleActionAdapter, ImmutableModuleIdentifierFactory moduleIdentifierFactory) { this.instantiator = instantiator; this.ruleActionAdapter = ruleActionAdapter; this.moduleIdentifierNotationParser = NotationParserBuilder .toType(ModuleIdentifier.class) .converter(new ModuleIdentifierNotationConverter(moduleIdentifierFactory)) .toComposite(); } public DefaultComponentMetadataHandler(Instantiator instantiator, ImmutableModuleIdentifierFactory moduleIdentifierFactory) { this(instantiator, createAdapter(), moduleIdentifierFactory); } private static RuleActionAdapter<ComponentMetadataDetails> createAdapter() { RuleActionValidator<ComponentMetadataDetails> ruleActionValidator = new DefaultRuleActionValidator<ComponentMetadataDetails>(VALIDATOR_PARAM_LIST); return new DefaultRuleActionAdapter<ComponentMetadataDetails>(ruleActionValidator, ADAPTER_NAME); } private ComponentMetadataHandler addRule(SpecRuleAction<? super ComponentMetadataDetails> ruleAction) { rules.add(ruleAction); return this; } private SpecRuleAction<? super ComponentMetadataDetails> createAllSpecRuleAction(RuleAction<? super ComponentMetadataDetails> ruleAction) { return new SpecRuleAction<ComponentMetadataDetails>(ruleAction, Specs.<ComponentMetadataDetails>satisfyAll()); } private SpecRuleAction<? super ComponentMetadataDetails> createSpecRuleActionForModule(Object id, RuleAction<? super ComponentMetadataDetails> ruleAction) { ModuleIdentifier moduleIdentifier; try { moduleIdentifier = moduleIdentifierNotationParser.parseNotation(id); } catch (UnsupportedNotationException e) { throw new InvalidUserCodeException(String.format(INVALID_SPEC_ERROR, id == null ? "null" : id.toString()), e); } Spec<ComponentMetadataDetails> spec = new ComponentMetadataDetailsMatchingSpec(moduleIdentifier); return new SpecRuleAction<ComponentMetadataDetails>(ruleAction, spec); } public ComponentMetadataHandler all(Action<? super ComponentMetadataDetails> rule) { return addRule(createAllSpecRuleAction(ruleActionAdapter.createFromAction(rule))); } public ComponentMetadataHandler all(Closure<?> rule) { return addRule(createAllSpecRuleAction(ruleActionAdapter.createFromClosure(ComponentMetadataDetails.class, rule))); } public ComponentMetadataHandler all(Object ruleSource) { return addRule(createAllSpecRuleAction(ruleActionAdapter.createFromRuleSource(ComponentMetadataDetails.class, ruleSource))); } public ComponentMetadataHandler withModule(Object id, Action<? super ComponentMetadataDetails> rule) { return addRule(createSpecRuleActionForModule(id, ruleActionAdapter.createFromAction(rule))); } public ComponentMetadataHandler withModule(Object id, Closure<?> rule) { return addRule(createSpecRuleActionForModule(id, ruleActionAdapter.createFromClosure(ComponentMetadataDetails.class, rule))); } public ComponentMetadataHandler withModule(Object id, Object ruleSource) { return addRule(createSpecRuleActionForModule(id, ruleActionAdapter.createFromRuleSource(ComponentMetadataDetails.class, ruleSource))); } public ModuleComponentResolveMetadata processMetadata(ModuleComponentResolveMetadata metadata) { ModuleComponentResolveMetadata updatedMetadata; if (rules.isEmpty()) { updatedMetadata = metadata; } else { MutableModuleComponentResolveMetadata mutableMetadata = metadata.asMutable(); ComponentMetadataDetails details = instantiator.newInstance(ComponentMetadataDetailsAdapter.class, mutableMetadata); processAllRules(metadata, details); updatedMetadata = mutableMetadata.asImmutable(); } if (!updatedMetadata.getStatusScheme().contains(updatedMetadata.getStatus())) { throw new ModuleVersionResolveException(updatedMetadata.getId(), String.format("Unexpected status '%s' specified for %s. Expected one of: %s", updatedMetadata.getStatus(), updatedMetadata.getComponentId().getDisplayName(), updatedMetadata.getStatusScheme())); } return updatedMetadata; } private void processAllRules(ModuleComponentResolveMetadata metadata, ComponentMetadataDetails details) { for (SpecRuleAction<? super ComponentMetadataDetails> rule : rules) { processRule(rule, metadata, details); } } private void processRule(SpecRuleAction<? super ComponentMetadataDetails> specRuleAction, ModuleComponentResolveMetadata metadata, final ComponentMetadataDetails details) { if (!specRuleAction.getSpec().isSatisfiedBy(details)) { return; } final List<Object> inputs = Lists.newArrayList(); final RuleAction<? super ComponentMetadataDetails> action = specRuleAction.getAction(); for (Class<?> inputType : action.getInputTypes()) { if (inputType == IvyModuleDescriptor.class) { // Ignore the rule if it expects Ivy metadata and this isn't an Ivy module if (!(metadata instanceof IvyModuleResolveMetadata)) { return; } IvyModuleResolveMetadata ivyMetadata = (IvyModuleResolveMetadata) metadata; inputs.add(new DefaultIvyModuleDescriptor(ivyMetadata.getExtraInfo(), ivyMetadata.getBranch(), ivyMetadata.getStatus())); continue; } // We've already validated the inputs: should never get here. throw new IllegalStateException(); } try { synchronized (this) { action.execute(details, inputs); } } catch (Exception e) { throw new InvalidUserCodeException(String.format("There was an error while evaluating a component metadata rule for %s.", details.getId()), e); } } static class ComponentMetadataDetailsMatchingSpec implements Spec<ComponentMetadataDetails> { private ModuleIdentifier target; ComponentMetadataDetailsMatchingSpec(ModuleIdentifier target) { this.target = target; } public boolean isSatisfiedBy(ComponentMetadataDetails componentMetadataDetails) { return componentMetadataDetails.getId().getGroup().equals(target.getGroup()) && componentMetadataDetails.getId().getName().equals(target.getName()); } } }