/*
* Copyright 2016 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.ivyresolve.parser;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
import org.apache.ivy.core.module.descriptor.DependencyArtifactDescriptor;
import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
import org.apache.ivy.core.module.descriptor.ExcludeRule;
import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
import org.apache.ivy.core.module.id.ArtifactId;
import org.apache.ivy.core.module.id.ModuleRevisionId;
import org.gradle.api.artifacts.ModuleVersionSelector;
import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
import org.gradle.api.internal.artifacts.DefaultModuleVersionSelector;
import org.gradle.api.internal.artifacts.ImmutableModuleIdentifierFactory;
import org.gradle.api.internal.artifacts.ivyservice.NamespaceId;
import org.gradle.internal.Cast;
import org.gradle.internal.UncheckedException;
import org.gradle.internal.component.external.descriptor.Artifact;
import org.gradle.internal.component.external.descriptor.Configuration;
import org.gradle.internal.component.external.descriptor.DefaultExclude;
import org.gradle.internal.component.external.descriptor.ModuleDescriptorState;
import org.gradle.internal.component.external.descriptor.MutableModuleDescriptorState;
import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier;
import org.gradle.internal.component.external.model.IvyDependencyMetadata;
import org.gradle.internal.component.model.DefaultIvyArtifactName;
import org.gradle.internal.component.model.Exclude;
import org.gradle.internal.component.model.IvyArtifactName;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
public class IvyModuleDescriptorConverter {
private static final String CLASSIFIER = "classifier";
private static final Field DEPENDENCY_CONFIG_FIELD;
static {
try {
DEPENDENCY_CONFIG_FIELD = DefaultDependencyDescriptor.class.getDeclaredField("confs");
DEPENDENCY_CONFIG_FIELD.setAccessible(true);
} catch (NoSuchFieldException e) {
throw UncheckedException.throwAsUncheckedException(e);
}
}
private final ImmutableModuleIdentifierFactory moduleIdentifierFactory;
public IvyModuleDescriptorConverter(ImmutableModuleIdentifierFactory moduleIdentifierFactory) {
this.moduleIdentifierFactory = moduleIdentifierFactory;
}
public ModuleDescriptorState forIvyModuleDescriptor(ModuleDescriptor ivyDescriptor) {
ModuleRevisionId moduleRevisionId = ivyDescriptor.getModuleRevisionId();
ModuleComponentIdentifier componentIdentifier = DefaultModuleComponentIdentifier.newId(moduleRevisionId.getOrganisation(), moduleRevisionId.getName(), moduleRevisionId.getRevision());
MutableModuleDescriptorState state = new MutableModuleDescriptorState(componentIdentifier, ivyDescriptor.getStatus(), ivyDescriptor.isDefault());
state.setBranch(moduleRevisionId.getBranch());
state.setDescription(ivyDescriptor.getDescription());
state.setPublicationDate(ivyDescriptor.getPublicationDate());
Map<NamespaceId, String> extraInfo = Cast.uncheckedCast(ivyDescriptor.getExtraInfo());
state.getExtraInfo().putAll(extraInfo);
for (ExcludeRule excludeRule : ivyDescriptor.getAllExcludeRules()) {
addExcludeRule(state, excludeRule);
}
return state;
}
public List<IvyDependencyMetadata> extractDependencies(ModuleDescriptor ivyDescriptor) {
List<IvyDependencyMetadata> result = Lists.newArrayListWithCapacity(ivyDescriptor.getDependencies().length);
for (DependencyDescriptor dependencyDescriptor : ivyDescriptor.getDependencies()) {
addDependency(result, dependencyDescriptor);
}
return result;
}
public List<Configuration> extractConfigurations(ModuleDescriptor ivyDescriptor) {
List<Configuration> result = Lists.newArrayListWithCapacity(ivyDescriptor.getConfigurations().length);
for (org.apache.ivy.core.module.descriptor.Configuration ivyConfiguration : ivyDescriptor.getConfigurations()) {
addConfiguration(result, ivyConfiguration);
}
return result;
}
private static void addConfiguration(List<Configuration> result, org.apache.ivy.core.module.descriptor.Configuration configuration) {
String name = configuration.getName();
boolean transitive = configuration.isTransitive();
boolean visible = configuration.getVisibility() == org.apache.ivy.core.module.descriptor.Configuration.Visibility.PUBLIC;
List<String> extendsFrom = Lists.newArrayList(configuration.getExtends());
result.add(new Configuration(name, transitive, visible, extendsFrom));
}
private void addExcludeRule(MutableModuleDescriptorState state, ExcludeRule excludeRule) {
state.addExclude(forIvyExclude(excludeRule));
}
private void addDependency(List<IvyDependencyMetadata> result, DependencyDescriptor dependencyDescriptor) {
ModuleRevisionId revisionId = dependencyDescriptor.getDependencyRevisionId();
ModuleVersionSelector requested = DefaultModuleVersionSelector.newSelector(revisionId.getOrganisation(), revisionId.getName(), revisionId.getRevision());
ListMultimap<String, String> configMappings = ArrayListMultimap.create();
for (Map.Entry<String, List<String>> entry : readConfigMappings(dependencyDescriptor).entrySet()) {
configMappings.putAll(entry.getKey(), entry.getValue());
}
List<Artifact> artifacts = Lists.newArrayList();
for (DependencyArtifactDescriptor ivyArtifact : dependencyDescriptor.getAllDependencyArtifacts()) {
IvyArtifactName ivyArtifactName = new DefaultIvyArtifactName(ivyArtifact.getName(), ivyArtifact.getType(), ivyArtifact.getExt(), (String) ivyArtifact.getExtraAttributes().get(CLASSIFIER));
artifacts.add(new Artifact(ivyArtifactName, Sets.newHashSet(ivyArtifact.getConfigurations())));
}
List<Exclude> excludes = Lists.newArrayList();
for (ExcludeRule excludeRule : dependencyDescriptor.getAllExcludeRules()) {
excludes.add(forIvyExclude(excludeRule));
}
result.add(new IvyDependencyMetadata(
requested,
dependencyDescriptor.getDynamicConstraintDependencyRevisionId().getRevision(),
false,
dependencyDescriptor.isChanging(),
dependencyDescriptor.isTransitive(),
configMappings,
artifacts,
excludes));
}
private Exclude forIvyExclude(org.apache.ivy.core.module.descriptor.ExcludeRule excludeRule) {
ArtifactId id = excludeRule.getId();
return new DefaultExclude(
moduleIdentifierFactory.module(id.getModuleId().getOrganisation(), id.getModuleId().getName()), id.getName(), id.getType(), id.getExt(),
excludeRule.getConfigurations(), excludeRule.getMatcher().getName());
}
// TODO We should get rid of this reflection (will need to reimplement the parser to create a ModuleDescriptorState directly)
private static Map<String, List<String>> readConfigMappings(DependencyDescriptor dependencyDescriptor) {
if (dependencyDescriptor instanceof DefaultDependencyDescriptor) {
try {
return (Map<String, List<String>>) DEPENDENCY_CONFIG_FIELD.get(dependencyDescriptor);
} catch (IllegalAccessException e) {
throw UncheckedException.throwAsUncheckedException(e);
}
}
String[] modConfs = dependencyDescriptor.getModuleConfigurations();
Map<String, List<String>> results = Maps.newLinkedHashMap();
for (String modConf : modConfs) {
results.put(modConf, Arrays.asList(dependencyDescriptor.getDependencyConfigurations(modConfs)));
}
return results;
}
}