/*
* 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.api.internal.artifacts.ivyservice.resolutionstrategy;
import com.google.common.collect.Lists;
import groovy.lang.Closure;
import org.gradle.api.Action;
import org.gradle.api.InvalidUserCodeException;
import org.gradle.api.artifacts.ComponentMetadata;
import org.gradle.api.artifacts.ComponentSelection;
import org.gradle.api.artifacts.ComponentSelectionRules;
import org.gradle.api.artifacts.ModuleIdentifier;
import org.gradle.api.artifacts.ivy.IvyModuleDescriptor;
import org.gradle.api.internal.artifacts.ComponentSelectionRulesInternal;
import org.gradle.api.internal.artifacts.ImmutableModuleIdentifierFactory;
import org.gradle.api.internal.artifacts.configurations.MutationValidator;
import org.gradle.api.internal.notations.ModuleIdentifierNotationConverter;
import org.gradle.api.specs.Spec;
import org.gradle.api.specs.Specs;
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.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import static org.gradle.api.internal.artifacts.configurations.MutationValidator.MutationType.STRATEGY;
public class DefaultComponentSelectionRules implements ComponentSelectionRulesInternal {
private static final String INVALID_SPEC_ERROR = "Could not add a component selection rule for module '%s'.";
private MutationValidator mutationValidator = MutationValidator.IGNORE;
private Set<SpecRuleAction<? super ComponentSelection>> rules;
private final RuleActionAdapter<ComponentSelection> ruleActionAdapter;
private final NotationParser<Object, ModuleIdentifier> moduleIdentifierNotationParser;
public DefaultComponentSelectionRules(ImmutableModuleIdentifierFactory moduleIdentifierFactory) {
this(moduleIdentifierFactory, createAdapter());
}
protected DefaultComponentSelectionRules(ImmutableModuleIdentifierFactory moduleIdentifierFactory, RuleActionAdapter<ComponentSelection> ruleActionAdapter) {
this.ruleActionAdapter = ruleActionAdapter;
this.moduleIdentifierNotationParser = NotationParserBuilder
.toType(ModuleIdentifier.class)
.converter(new ModuleIdentifierNotationConverter(moduleIdentifierFactory))
.toComposite();
}
/**
* Sets the validator to invoke prior to each mutation.
*/
public void setMutationValidator(MutationValidator mutationValidator) {
this.mutationValidator = mutationValidator;
}
private static RuleActionAdapter<ComponentSelection> createAdapter() {
RuleActionValidator<ComponentSelection> ruleActionValidator = new DefaultRuleActionValidator<ComponentSelection>(Lists.newArrayList(ComponentMetadata.class, IvyModuleDescriptor.class));
return new DefaultRuleActionAdapter<ComponentSelection>(ruleActionValidator, "ComponentSelectionRules");
}
public Collection<SpecRuleAction<? super ComponentSelection>> getRules() {
return rules != null ? rules : Collections.<SpecRuleAction<? super ComponentSelection>>emptySet();
}
public ComponentSelectionRules all(Action<? super ComponentSelection> selectionAction) {
return addRule(createAllSpecRulesAction(ruleActionAdapter.createFromAction(selectionAction)));
}
public ComponentSelectionRules all(Closure<?> closure) {
return addRule(createAllSpecRulesAction(ruleActionAdapter.createFromClosure(ComponentSelection.class, closure)));
}
public ComponentSelectionRules all(Object ruleSource) {
return addRule(createAllSpecRulesAction(ruleActionAdapter.createFromRuleSource(ComponentSelection.class, ruleSource)));
}
public ComponentSelectionRules withModule(Object id, Action<? super ComponentSelection> selectionAction) {
return addRule(createSpecRuleActionFromId(id, ruleActionAdapter.createFromAction(selectionAction)));
}
public ComponentSelectionRules withModule(Object id, Closure<?> closure) {
return addRule(createSpecRuleActionFromId(id, ruleActionAdapter.createFromClosure(ComponentSelection.class, closure)));
}
public ComponentSelectionRules withModule(Object id, Object ruleSource) {
return addRule(createSpecRuleActionFromId(id, ruleActionAdapter.createFromRuleSource(ComponentSelection.class, ruleSource)));
}
public ComponentSelectionRules addRule(SpecRuleAction<? super ComponentSelection> specRuleAction) {
mutationValidator.validateMutation(STRATEGY);
if (rules == null) {
rules = new HashSet<SpecRuleAction<? super ComponentSelection>>();
}
rules.add(specRuleAction);
return this;
}
@Override
public ComponentSelectionRules addRule(RuleAction<? super ComponentSelection> specRuleAction) {
return addRule(createAllSpecRulesAction(specRuleAction));
}
private SpecRuleAction<? super ComponentSelection> createSpecRuleActionFromId(Object id, RuleAction<? super ComponentSelection> ruleAction) {
final 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<ComponentSelection> spec = new ComponentSelectionMatchingSpec(moduleIdentifier);
return new SpecRuleAction<ComponentSelection>(ruleAction, spec);
}
private SpecRuleAction<? super ComponentSelection> createAllSpecRulesAction(RuleAction<? super ComponentSelection> ruleAction) {
return new SpecRuleAction<ComponentSelection>(ruleAction, Specs.<ComponentSelection>satisfyAll());
}
static class ComponentSelectionMatchingSpec implements Spec<ComponentSelection> {
final ModuleIdentifier target;
private ComponentSelectionMatchingSpec(ModuleIdentifier target) {
this.target = target;
}
public boolean isSatisfiedBy(ComponentSelection selection) {
return selection.getCandidate().getGroup().equals(target.getGroup()) && selection.getCandidate().getModule().equals(target.getName());
}
}
}