/*
* Copyright 2015 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.component.model;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.artifacts.ModuleVersionSelector;
import org.gradle.api.artifacts.component.ComponentSelector;
import org.gradle.api.artifacts.component.ModuleComponentSelector;
import org.gradle.api.artifacts.component.ProjectComponentSelector;
import org.gradle.api.attributes.AttributeContainer;
import org.gradle.api.internal.artifacts.DefaultModuleVersionSelector;
import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusion;
import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusions;
import org.gradle.api.internal.attributes.AttributeContainerInternal;
import org.gradle.api.internal.attributes.AttributesSchemaInternal;
import org.gradle.internal.DisplayName;
import org.gradle.internal.component.AmbiguousConfigurationSelectionException;
import org.gradle.internal.component.IncompatibleConfigurationSelectionException;
import org.gradle.internal.component.NoMatchingConfigurationSelectionException;
import org.gradle.internal.component.external.model.DefaultModuleComponentSelector;
import org.gradle.internal.component.local.model.LocalComponentArtifactMetadata;
import org.gradle.internal.component.local.model.LocalConfigurationMetadata;
import org.gradle.internal.component.local.model.LocalFileDependencyMetadata;
import org.gradle.internal.exceptions.ConfigurationNotConsumableException;
import org.gradle.util.GUtil;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
public class LocalComponentDependencyMetadata implements LocalOriginDependencyMetadata {
private final ComponentSelector selector;
private final ModuleVersionSelector requested;
private final String moduleConfiguration;
private final String dependencyConfiguration;
private final List<Exclude> excludes;
private final Set<IvyArtifactName> artifactNames;
private final boolean force;
private final boolean changing;
private final boolean transitive;
private final AttributeContainer moduleAttributes;
public LocalComponentDependencyMetadata(ComponentSelector selector, ModuleVersionSelector requested,
String moduleConfiguration,
AttributeContainer moduleAttributes,
String dependencyConfiguration,
Set<IvyArtifactName> artifactNames, List<Exclude> excludes,
boolean force, boolean changing, boolean transitive) {
this.selector = selector;
this.requested = requested;
this.moduleConfiguration = moduleConfiguration;
this.moduleAttributes = moduleAttributes;
this.dependencyConfiguration = dependencyConfiguration;
this.artifactNames = artifactNames;
this.excludes = excludes;
this.force = force;
this.changing = changing;
this.transitive = transitive;
}
@Override
public String toString() {
return "dependency: " + requested + " from-conf: " + moduleConfiguration + " to-conf: " + dependencyConfiguration;
}
@Override
public ModuleVersionSelector getRequested() {
return requested;
}
@Override
public ComponentSelector getSelector() {
return selector;
}
@Override
public String getModuleConfiguration() {
return moduleConfiguration;
}
@Override
public String getDependencyConfiguration() {
return getOrDefaultConfiguration(dependencyConfiguration);
}
@Override
public Set<ConfigurationMetadata> selectConfigurations(ComponentResolveMetadata fromComponent, ConfigurationMetadata fromConfiguration, ComponentResolveMetadata targetComponent, AttributesSchemaInternal consumerSchema) {
AttributeContainerInternal fromConfigurationAttributes = fromConfiguration.getAttributes();
boolean consumerHasAttributes = !fromConfigurationAttributes.isEmpty();
boolean useConfigurationAttributes = dependencyConfiguration == null && consumerHasAttributes;
AttributesSchemaInternal producerAttributeSchema = targetComponent.getAttributesSchema();
if (useConfigurationAttributes) {
List<? extends ConfigurationMetadata> consumableConfigurations = targetComponent.getConsumableConfigurationsHavingAttributes();
AttributeMatcher attributeMatcher = consumerSchema.withProducer(producerAttributeSchema);
List<? extends ConfigurationMetadata> matches = attributeMatcher.matches(consumableConfigurations, fromConfigurationAttributes);
if (matches.size() == 1) {
return ImmutableSet.of(ClientAttributesPreservingConfigurationMetadata.wrapIfLocal(matches.get(0), fromConfigurationAttributes));
} else if (!matches.isEmpty()) {
throw new AmbiguousConfigurationSelectionException(fromConfigurationAttributes, attributeMatcher, matches, targetComponent);
}
}
String targetConfiguration = GUtil.elvis(dependencyConfiguration, Dependency.DEFAULT_CONFIGURATION);
ConfigurationMetadata toConfiguration = targetComponent.getConfiguration(targetConfiguration);
if (toConfiguration == null) {
throw new ConfigurationNotFoundException(fromComponent.getComponentId(), moduleConfiguration, targetConfiguration, targetComponent.getComponentId());
}
if (!toConfiguration.isCanBeConsumed()) {
if (dependencyConfiguration == null) {
// this was a fallback to `default`, and `default` is not consumable
Set<String> configurationNames = Sets.newTreeSet();
configurationNames.addAll(targetComponent.getConfigurationNames());
throw new NoMatchingConfigurationSelectionException(fromConfigurationAttributes, consumerSchema.withProducer(producerAttributeSchema), targetComponent, Lists.newArrayList(configurationNames));
}
// explicit configuration selection
throw new ConfigurationNotConsumableException(targetComponent.toString(), toConfiguration.getName());
}
ConfigurationMetadata delegate = toConfiguration;
if (consumerHasAttributes) {
if (!delegate.getAttributes().isEmpty()) {
// need to validate that the selected configuration still matches the consumer attributes
if (!consumerSchema.withProducer(producerAttributeSchema).isMatching(delegate.getAttributes(), fromConfigurationAttributes)) {
throw new IncompatibleConfigurationSelectionException(fromConfigurationAttributes, consumerSchema.withProducer(producerAttributeSchema), targetComponent, targetConfiguration);
}
}
}
if (useConfigurationAttributes) {
delegate = ClientAttributesPreservingConfigurationMetadata.wrapIfLocal(delegate, fromConfigurationAttributes);
}
return ImmutableSet.of(delegate);
}
private static String getOrDefaultConfiguration(String configuration) {
return GUtil.elvis(configuration, Dependency.DEFAULT_CONFIGURATION);
}
@Override
public Set<String> getModuleConfigurations() {
return ImmutableSet.of(getOrDefaultConfiguration(moduleConfiguration));
}
@Override
public List<Exclude> getExcludes() {
return excludes;
}
@Override
public List<Exclude> getExcludes(Collection<String> configurations) {
return excludes;
}
@Override
public boolean isChanging() {
return changing;
}
@Override
public boolean isTransitive() {
return transitive;
}
@Override
public boolean isForce() {
return force;
}
@Override
public String getDynamicConstraintVersion() {
return requested.getVersion();
}
@Override
public Set<ComponentArtifactMetadata> getArtifacts(ConfigurationMetadata fromConfiguration, ConfigurationMetadata toConfiguration) {
if (artifactNames.isEmpty()) {
return Collections.emptySet();
}
Set<ComponentArtifactMetadata> artifacts = new LinkedHashSet<ComponentArtifactMetadata>();
for (IvyArtifactName artifactName : artifactNames) {
artifacts.add(toConfiguration.artifact(artifactName));
}
return artifacts;
}
@Override
public Set<IvyArtifactName> getArtifacts() {
return artifactNames;
}
@Override
public LocalOriginDependencyMetadata withRequestedVersion(String requestedVersion) {
if (requestedVersion.equals(requested.getVersion())) {
return this;
}
ModuleVersionSelector newRequested = DefaultModuleVersionSelector.newSelector(requested.getGroup(), requested.getName(), requestedVersion);
ComponentSelector newSelector = DefaultModuleComponentSelector.newSelector(newRequested);
return copyWithTarget(newSelector, newRequested);
}
@Override
public LocalOriginDependencyMetadata withTarget(ComponentSelector target) {
if (target instanceof ModuleComponentSelector) {
ModuleComponentSelector moduleTarget = (ModuleComponentSelector) target;
ModuleVersionSelector requestedVersion = DefaultModuleVersionSelector.newSelector(moduleTarget.getGroup(), moduleTarget.getModule(), moduleTarget.getVersion());
if (selector.equals(target) && requested.equals(requestedVersion)) {
return this;
}
return copyWithTarget(moduleTarget, requestedVersion);
} else if (target instanceof ProjectComponentSelector) {
if (target.equals(selector)) {
return this;
}
return copyWithTarget(target, requested);
} else {
throw new AssertionError("Invalid component selector type for substitution: " + target);
}
}
private LocalOriginDependencyMetadata copyWithTarget(ComponentSelector selector, ModuleVersionSelector requested) {
return new LocalComponentDependencyMetadata(selector, requested, moduleConfiguration, moduleAttributes, dependencyConfiguration, artifactNames, excludes, force, changing, transitive);
}
private static class ClientAttributesPreservingConfigurationMetadata implements LocalConfigurationMetadata {
private final LocalConfigurationMetadata delegate;
private final AttributeContainerInternal attributes;
private static ConfigurationMetadata wrapIfLocal(ConfigurationMetadata md, AttributeContainerInternal attributes) {
if (md instanceof LocalConfigurationMetadata) {
return new ClientAttributesPreservingConfigurationMetadata((LocalConfigurationMetadata) md, attributes);
}
return md;
}
private ClientAttributesPreservingConfigurationMetadata(LocalConfigurationMetadata delegate, AttributeContainerInternal attributes) {
this.delegate = delegate;
this.attributes = attributes;
}
@Override
public AttributeContainerInternal getAttributes() {
return attributes;
}
@Override
public boolean isCanBeConsumed() {
return delegate.isCanBeConsumed();
}
@Override
public boolean isCanBeResolved() {
return delegate.isCanBeResolved();
}
@Override
public Set<String> getHierarchy() {
return delegate.getHierarchy();
}
@Override
public String getName() {
return delegate.getName();
}
@Override
public DisplayName asDescribable() {
return delegate.asDescribable();
}
@Override
public List<? extends LocalOriginDependencyMetadata> getDependencies() {
return delegate.getDependencies();
}
@Override
public Set<? extends LocalComponentArtifactMetadata> getArtifacts() {
return delegate.getArtifacts();
}
@Override
public Set<? extends VariantMetadata> getVariants() {
return delegate.getVariants();
}
@Override
public ModuleExclusion getExclusions(ModuleExclusions moduleExclusions) {
return delegate.getExclusions(moduleExclusions);
}
@Override
public boolean isTransitive() {
return delegate.isTransitive();
}
@Override
public boolean isVisible() {
return delegate.isVisible();
}
@Override
public ComponentArtifactMetadata artifact(IvyArtifactName artifact) {
return delegate.artifact(artifact);
}
@Override
public String getDescription() {
return delegate.getDescription();
}
@Override
public Set<String> getExtendsFrom() {
return delegate.getExtendsFrom();
}
@Override
public Set<LocalFileDependencyMetadata> getFiles() {
return delegate.getFiles();
}
}
}