/*
* 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.internal.component.local.model;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.SetMultimap;
import org.gradle.api.Transformer;
import org.gradle.api.artifacts.ModuleVersionIdentifier;
import org.gradle.api.artifacts.PublishArtifact;
import org.gradle.api.artifacts.component.ComponentIdentifier;
import org.gradle.api.internal.artifacts.configurations.OutgoingVariant;
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.Describables;
import org.gradle.internal.component.model.ComponentArtifactMetadata;
import org.gradle.internal.component.model.ComponentResolveMetadata;
import org.gradle.internal.component.model.ConfigurationMetadata;
import org.gradle.internal.component.model.DefaultVariantMetadata;
import org.gradle.internal.component.model.Exclude;
import org.gradle.internal.component.model.IvyArtifactName;
import org.gradle.internal.component.model.LocalOriginDependencyMetadata;
import org.gradle.internal.component.model.ModuleSource;
import org.gradle.internal.component.model.VariantMetadata;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class DefaultLocalComponentMetadata implements LocalComponentMetadata, BuildableLocalComponentMetadata {
private final Map<String, DefaultLocalConfigurationMetadata> allConfigurations = Maps.newLinkedHashMap();
private final Multimap<String, LocalComponentArtifactMetadata> allArtifacts = ArrayListMultimap.create();
private final Multimap<String, LocalFileDependencyMetadata> allFiles = ArrayListMultimap.create();
private final SetMultimap<String, DefaultVariantMetadata> allVariants = LinkedHashMultimap.create();
private final List<LocalOriginDependencyMetadata> allDependencies = Lists.newArrayList();
private final List<Exclude> allExcludes = Lists.newArrayList();
private final ModuleVersionIdentifier id;
private final ComponentIdentifier componentIdentifier;
private final String status;
private final AttributesSchemaInternal attributesSchema;
private List<ConfigurationMetadata> consumableConfigurations;
public DefaultLocalComponentMetadata(ModuleVersionIdentifier id, ComponentIdentifier componentIdentifier, String status, AttributesSchemaInternal attributesSchema) {
this.id = id;
this.componentIdentifier = componentIdentifier;
this.status = status;
this.attributesSchema = attributesSchema;
}
@Override
public ModuleVersionIdentifier getId() {
return id;
}
/**
* Creates a copy of this metadata, transforming the artifacts and dependencies of this component.
*/
public DefaultLocalComponentMetadata copy(ComponentIdentifier componentIdentifier, Transformer<LocalComponentArtifactMetadata, LocalComponentArtifactMetadata> artifacts, Transformer<LocalOriginDependencyMetadata, LocalOriginDependencyMetadata> dependencies) {
DefaultLocalComponentMetadata copy = new DefaultLocalComponentMetadata(id, componentIdentifier, status, attributesSchema);
for (DefaultLocalConfigurationMetadata configuration : allConfigurations.values()) {
copy.addConfiguration(configuration.getName(), configuration.description, configuration.extendsFrom, configuration.hierarchy, configuration.visible, configuration.transitive, configuration.attributes, configuration.canBeConsumed, configuration.canBeResolved);
}
// Artifacts
// Keep track of transformed artifacts as a given artifact may appear in multiple variants
Map<LocalComponentArtifactMetadata, LocalComponentArtifactMetadata> transformedArtifacts = new HashMap<LocalComponentArtifactMetadata, LocalComponentArtifactMetadata>();
for (Map.Entry<String, LocalComponentArtifactMetadata> entry : allArtifacts.entries()) {
LocalComponentArtifactMetadata oldArtifact = entry.getValue();
LocalComponentArtifactMetadata newArtifact = copyArtifact(oldArtifact, artifacts, transformedArtifacts);
copy.allArtifacts.put(entry.getKey(), newArtifact);
}
// Variants
for (Map.Entry<String, DefaultVariantMetadata> entry : allVariants.entries()) {
DefaultVariantMetadata oldVariant = entry.getValue();
Set<LocalComponentArtifactMetadata> newArtifacts = new LinkedHashSet<LocalComponentArtifactMetadata>(oldVariant.getArtifacts().size());
for (ComponentArtifactMetadata oldArtifact : oldVariant.getArtifacts()) {
newArtifacts.add(copyArtifact((LocalComponentArtifactMetadata) oldArtifact, artifacts, transformedArtifacts));
}
copy.allVariants.put(entry.getKey(), new DefaultVariantMetadata(oldVariant.asDescribable(), oldVariant.getAttributes(), newArtifacts));
}
// Don't include file dependencies
// Dependencies
for (LocalOriginDependencyMetadata oldDependency : allDependencies) {
copy.allDependencies.add(dependencies.transform(oldDependency));
}
// Exclude rules
copy.allExcludes.addAll(allExcludes);
return copy;
}
private LocalComponentArtifactMetadata copyArtifact(LocalComponentArtifactMetadata oldArtifact, Transformer<LocalComponentArtifactMetadata, LocalComponentArtifactMetadata> transformer, Map<LocalComponentArtifactMetadata, LocalComponentArtifactMetadata> transformedArtifacts) {
LocalComponentArtifactMetadata newArtifact = transformedArtifacts.get(oldArtifact);
if (newArtifact == null) {
newArtifact = transformer.transform(oldArtifact);
transformedArtifacts.put(oldArtifact, newArtifact);
}
return newArtifact;
}
@Override
public void addArtifacts(String configuration, Iterable<? extends PublishArtifact> artifacts) {
for (PublishArtifact artifact : artifacts) {
LocalComponentArtifactMetadata artifactMetadata = new PublishArtifactLocalArtifactMetadata(componentIdentifier, artifact);
addArtifact(configuration, artifactMetadata);
}
}
public void addArtifact(String configuration, LocalComponentArtifactMetadata artifactMetadata) {
allArtifacts.put(configuration, artifactMetadata);
}
@Override
public void addVariant(String configuration, OutgoingVariant variant) {
Set<LocalComponentArtifactMetadata> artifacts;
if (variant.getArtifacts().isEmpty()) {
artifacts = ImmutableSet.of();
} else {
ImmutableSet.Builder<LocalComponentArtifactMetadata> builder = ImmutableSet.builder();
for (PublishArtifact artifact : variant.getArtifacts()) {
builder.add(new PublishArtifactLocalArtifactMetadata(componentIdentifier, artifact));
}
artifacts = builder.build();
}
allVariants.put(configuration, new DefaultVariantMetadata(variant.asDescribable(), variant.getAttributes().asImmutable(), artifacts));
}
@Override
public void addFiles(String configuration, LocalFileDependencyMetadata files) {
allFiles.put(configuration, files);
}
@Override
public void addConfiguration(String name, String description, Set<String> extendsFrom, Set<String> hierarchy, boolean visible, boolean transitive, AttributeContainerInternal attributes, boolean canBeConsumed, boolean canBeResolved) {
assert hierarchy.contains(name);
DefaultLocalConfigurationMetadata conf = new DefaultLocalConfigurationMetadata(name, description, visible, transitive, extendsFrom, hierarchy, attributes, canBeConsumed, canBeResolved);
allConfigurations.put(name, conf);
}
@Override
public void addDependency(LocalOriginDependencyMetadata dependency) {
allDependencies.add(dependency);
}
@Override
public void addExclude(Exclude exclude) {
allExcludes.add(exclude);
}
@Override
public String toString() {
return componentIdentifier.getDisplayName();
}
@Override
public ModuleSource getSource() {
return null;
}
@Override
public ComponentResolveMetadata withSource(ModuleSource source) {
throw new UnsupportedOperationException();
}
@Override
public boolean isGenerated() {
return false;
}
@Override
public boolean isChanging() {
return false;
}
@Override
public String getStatus() {
return status;
}
@Override
public List<String> getStatusScheme() {
return DEFAULT_STATUS_SCHEME;
}
@Override
public ComponentIdentifier getComponentId() {
return componentIdentifier;
}
@Override
public List<LocalOriginDependencyMetadata> getDependencies() {
return allDependencies;
}
public List<Exclude> getExcludeRules() {
return allExcludes;
}
@Override
public Set<String> getConfigurationNames() {
return allConfigurations.keySet();
}
@Override
public synchronized List<? extends ConfigurationMetadata> getConsumableConfigurationsHavingAttributes() {
if (consumableConfigurations == null) {
consumableConfigurations = Lists.newArrayListWithExpectedSize(allConfigurations.size());
for (DefaultLocalConfigurationMetadata metadata : allConfigurations.values()) {
if (metadata.isCanBeConsumed() && !metadata.getAttributes().isEmpty()) {
consumableConfigurations.add(metadata);
}
}
}
return consumableConfigurations;
}
@Override
public LocalConfigurationMetadata getConfiguration(final String name) {
return allConfigurations.get(name);
}
@Override
public AttributesSchemaInternal getAttributesSchema() {
return attributesSchema;
}
private class DefaultLocalConfigurationMetadata implements LocalConfigurationMetadata {
private final String name;
private final String description;
private final boolean transitive;
private final boolean visible;
private final Set<String> hierarchy;
private final Set<String> extendsFrom;
private final AttributeContainerInternal attributes;
private final boolean canBeConsumed;
private final boolean canBeResolved;
private List<LocalOriginDependencyMetadata> configurationDependencies;
private Set<LocalComponentArtifactMetadata> configurationArtifacts;
private Set<LocalFileDependencyMetadata> configurationFileDependencies;
private ModuleExclusion configurationExclude;
private DefaultLocalConfigurationMetadata(String name,
String description,
boolean visible,
boolean transitive,
Set<String> extendsFrom,
Set<String> hierarchy,
AttributeContainerInternal attributes,
boolean canBeConsumed,
boolean canBeResolved) {
this.name = name;
this.description = description;
this.transitive = transitive;
this.visible = visible;
this.hierarchy = hierarchy;
this.extendsFrom = extendsFrom;
this.attributes = attributes;
this.canBeConsumed = canBeConsumed;
this.canBeResolved = canBeResolved;
}
@Override
public String toString() {
return asDescribable().getDisplayName();
}
@Override
public DisplayName asDescribable() {
return Describables.of(componentIdentifier, "configuration", name);
}
public ComponentResolveMetadata getComponent() {
return DefaultLocalComponentMetadata.this;
}
@Override
public String getDescription() {
return description;
}
@Override
public Set<String> getExtendsFrom() {
return extendsFrom;
}
@Override
public String getName() {
return name;
}
@Override
public Set<String> getHierarchy() {
return hierarchy;
}
@Override
public boolean isTransitive() {
return transitive;
}
@Override
public boolean isVisible() {
return visible;
}
@Override
public AttributeContainerInternal getAttributes() {
return attributes;
}
@Override
public Set<? extends VariantMetadata> getVariants() {
return allVariants.get(name);
}
@Override
public Set<LocalFileDependencyMetadata> getFiles() {
if (configurationFileDependencies == null) {
if (allFiles.isEmpty()) {
configurationFileDependencies = ImmutableSet.of();
} else {
ImmutableSet.Builder<LocalFileDependencyMetadata> result = ImmutableSet.builder();
for (String confName : hierarchy) {
for (LocalFileDependencyMetadata files : allFiles.get(confName)) {
result.add(files);
}
}
configurationFileDependencies = result.build();
}
}
return configurationFileDependencies;
}
@Override
public boolean isCanBeConsumed() {
return canBeConsumed;
}
@Override
public boolean isCanBeResolved() {
return canBeResolved;
}
@Override
public List<? extends LocalOriginDependencyMetadata> getDependencies() {
if (configurationDependencies == null) {
if (allDependencies.isEmpty()) {
configurationDependencies = ImmutableList.of();
} else {
ImmutableList.Builder<LocalOriginDependencyMetadata> result = ImmutableList.builder();
for (LocalOriginDependencyMetadata dependency : allDependencies) {
if (include(dependency)) {
result.add(dependency);
}
}
configurationDependencies = result.build();
}
}
return configurationDependencies;
}
private boolean include(LocalOriginDependencyMetadata dependency) {
return hierarchy.contains(dependency.getModuleConfiguration());
}
@Override
public ModuleExclusion getExclusions(ModuleExclusions moduleExclusions) {
if (configurationExclude == null) {
if (allExcludes.isEmpty()) {
configurationExclude = ModuleExclusions.excludeNone();
} else {
List<Exclude> filtered = Lists.newArrayList();
for (Exclude exclude : allExcludes) {
for (String config : exclude.getConfigurations()) {
if (hierarchy.contains(config)) {
filtered.add(exclude);
break;
}
}
}
configurationExclude = moduleExclusions.excludeAny(filtered);
}
}
return configurationExclude;
}
@Override
public Set<? extends LocalComponentArtifactMetadata> getArtifacts() {
if (configurationArtifacts == null) {
if (allArtifacts.isEmpty()) {
configurationArtifacts = ImmutableSet.of();
} else {
ImmutableSet.Builder<LocalComponentArtifactMetadata> result = ImmutableSet.builder();
for (String config : hierarchy) {
result.addAll(allArtifacts.get(config));
}
configurationArtifacts = result.build();
}
}
return configurationArtifacts;
}
@Override
public ComponentArtifactMetadata artifact(IvyArtifactName ivyArtifactName) {
for (ComponentArtifactMetadata candidate : getArtifacts()) {
if (candidate.getName().equals(ivyArtifactName)) {
return candidate;
}
}
return new MissingLocalArtifactMetadata(componentIdentifier, ivyArtifactName);
}
}
}