blob: 8532a301a5261146dd175dbb8f5ee1dbc254cda9 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.ivy.plugins.parser.m2;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.ivy.Ivy;
import org.apache.ivy.core.cache.ArtifactOrigin;
import org.apache.ivy.core.module.descriptor.Artifact;
import org.apache.ivy.core.module.descriptor.Configuration;
import org.apache.ivy.core.module.descriptor.DefaultArtifact;
import org.apache.ivy.core.module.descriptor.DefaultDependencyArtifactDescriptor;
import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
import org.apache.ivy.core.module.descriptor.DefaultExcludeRule;
import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
import org.apache.ivy.core.module.descriptor.License;
import org.apache.ivy.core.module.descriptor.MDArtifact;
import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
import org.apache.ivy.core.module.descriptor.OverrideDependencyDescriptorMediator;
import org.apache.ivy.core.module.descriptor.Configuration.Visibility;
import org.apache.ivy.core.module.id.ArtifactId;
import org.apache.ivy.core.module.id.ModuleId;
import org.apache.ivy.core.module.id.ModuleRevisionId;
import org.apache.ivy.plugins.matcher.ExactPatternMatcher;
import org.apache.ivy.plugins.matcher.PatternMatcher;
import org.apache.ivy.plugins.parser.ModuleDescriptorParser;
import org.apache.ivy.plugins.parser.ParserSettings;
import org.apache.ivy.plugins.parser.m2.PomReader.PomDependencyData;
import org.apache.ivy.plugins.repository.Resource;
import org.apache.ivy.plugins.resolver.DependencyResolver;
import org.apache.ivy.util.Message;
/**
* Build a module descriptor. This class handle the complexity of the structure of an ivy
* ModuleDescriptor and isolate the PomModuleDescriptorParser from it.
*/
public class PomModuleDescriptorBuilder {
private static final int DEPENDENCY_MANAGEMENT_KEY_PARTS_COUNT = 4;
public static final Configuration[] MAVEN2_CONFIGURATIONS = new Configuration[] {
new Configuration("default", Visibility.PUBLIC,
"runtime dependencies and master artifact can be used with this conf",
new String[] {"runtime", "master"}, true, null),
new Configuration("master", Visibility.PUBLIC,
"contains only the artifact published by this module itself, "
+ "with no transitive dependencies",
new String[0], true, null),
new Configuration("compile", Visibility.PUBLIC,
"this is the default scope, used if none is specified. "
+ "Compile dependencies are available in all classpaths.",
new String[0], true, null),
new Configuration("provided", Visibility.PUBLIC,
"this is much like compile, but indicates you expect the JDK or a container "
+ "to provide it. "
+ "It is only available on the compilation classpath, and is not transitive.",
new String[0], true, null),
new Configuration("runtime", Visibility.PUBLIC,
"this scope indicates that the dependency is not required for compilation, "
+ "but is for execution. It is in the runtime and test classpaths, "
+ "but not the compile classpath.",
new String[] {"compile"}, true, null),
new Configuration("test", Visibility.PRIVATE,
"this scope indicates that the dependency is not required for normal use of "
+ "the application, and is only available for the test compilation and "
+ "execution phases.",
new String[] {"runtime"}, true, null),
new Configuration("system", Visibility.PUBLIC,
"this scope is similar to provided except that you have to provide the JAR "
+ "which contains it explicitly. The artifact is always available and is not "
+ "looked up in a repository.",
new String[0], true, null),
new Configuration("sources", Visibility.PUBLIC,
"this configuration contains the source artifact of this module, if any.",
new String[0], true, null),
new Configuration("javadoc", Visibility.PUBLIC,
"this configuration contains the javadoc artifact of this module, if any.",
new String[0], true, null),
new Configuration("optional", Visibility.PUBLIC,
"contains all optional dependencies", new String[0], true, null)
};
static final Map MAVEN2_CONF_MAPPING = new HashMap();
private static final String DEPENDENCY_MANAGEMENT = "m:dependency.management";
private static final String PROPERTIES = "m:properties";
private static final String EXTRA_INFO_DELIMITER = "__";
private static final Collection/*<String>*/ JAR_PACKAGINGS = Arrays.asList(
new String[] {"ejb", "bundle", "maven-plugin"});
static interface ConfMapper {
public void addMappingConfs(DefaultDependencyDescriptor dd, boolean isOptional);
}
static {
MAVEN2_CONF_MAPPING.put("compile", new ConfMapper() {
public void addMappingConfs(DefaultDependencyDescriptor dd, boolean isOptional) {
if (isOptional) {
dd.addDependencyConfiguration("optional", "compile(*)");
//dd.addDependencyConfiguration("optional", "provided(*)");
dd.addDependencyConfiguration("optional", "master(*)");
} else {
dd.addDependencyConfiguration("compile", "compile(*)");
//dd.addDependencyConfiguration("compile", "provided(*)");
dd.addDependencyConfiguration("compile", "master(*)");
dd.addDependencyConfiguration("runtime", "runtime(*)");
}
}
});
MAVEN2_CONF_MAPPING.put("provided", new ConfMapper() {
public void addMappingConfs(DefaultDependencyDescriptor dd, boolean isOptional) {
if (isOptional) {
dd.addDependencyConfiguration("optional", "compile(*)");
dd.addDependencyConfiguration("optional", "provided(*)");
dd.addDependencyConfiguration("optional", "runtime(*)");
dd.addDependencyConfiguration("optional", "master(*)");
} else {
dd.addDependencyConfiguration("provided", "compile(*)");
dd.addDependencyConfiguration("provided", "provided(*)");
dd.addDependencyConfiguration("provided", "runtime(*)");
dd.addDependencyConfiguration("provided", "master(*)");
}
}
});
MAVEN2_CONF_MAPPING.put("runtime", new ConfMapper() {
public void addMappingConfs(DefaultDependencyDescriptor dd, boolean isOptional) {
if (isOptional) {
dd.addDependencyConfiguration("optional", "compile(*)");
dd.addDependencyConfiguration("optional", "provided(*)");
dd.addDependencyConfiguration("optional", "master(*)");
} else {
dd.addDependencyConfiguration("runtime", "compile(*)");
dd.addDependencyConfiguration("runtime", "runtime(*)");
dd.addDependencyConfiguration("runtime", "master(*)");
}
}
});
MAVEN2_CONF_MAPPING.put("test", new ConfMapper() {
public void addMappingConfs(DefaultDependencyDescriptor dd, boolean isOptional) {
//optional doesn't make sense in the test scope
dd.addDependencyConfiguration("test", "runtime(*)");
dd.addDependencyConfiguration("test", "master(*)");
}
});
MAVEN2_CONF_MAPPING.put("system", new ConfMapper() {
public void addMappingConfs(DefaultDependencyDescriptor dd, boolean isOptional) {
//optional doesn't make sense in the system scope
dd.addDependencyConfiguration("system", "master(*)");
}
});
}
private final DefaultModuleDescriptor ivyModuleDescriptor;
private ModuleRevisionId mrid;
private DefaultArtifact mainArtifact;
private ParserSettings parserSettings;
public PomModuleDescriptorBuilder(
ModuleDescriptorParser parser, Resource res, ParserSettings ivySettings) {
ivyModuleDescriptor = new DefaultModuleDescriptor(parser, res);
ivyModuleDescriptor.setResolvedPublicationDate(new Date(res.getLastModified()));
for (int i = 0; i < MAVEN2_CONFIGURATIONS.length; i++) {
ivyModuleDescriptor.addConfiguration(MAVEN2_CONFIGURATIONS[i]);
}
ivyModuleDescriptor.setMappingOverride(true);
ivyModuleDescriptor.addExtraAttributeNamespace("m", Ivy.getIvyHomeURL() + "maven");
parserSettings = ivySettings;
}
public ModuleDescriptor getModuleDescriptor() {
return ivyModuleDescriptor;
}
public void setModuleRevId(String groupId, String artifactId, String version) {
mrid = ModuleRevisionId.newInstance(groupId, artifactId, version);
ivyModuleDescriptor.setModuleRevisionId(mrid);
if ((version == null) || version.endsWith("SNAPSHOT")) {
ivyModuleDescriptor.setStatus("integration");
} else {
ivyModuleDescriptor.setStatus("release");
}
}
public void setHomePage(String homePage) {
ivyModuleDescriptor.setHomePage(homePage);
}
public void setDescription(String description) {
ivyModuleDescriptor.setDescription(description);
}
public void setLicenses(License[] licenses) {
for (int i = 0; i < licenses.length; i++) {
ivyModuleDescriptor.addLicense(licenses[i]);
}
}
public void addMainArtifact(String artifactId, String packaging) {
String ext;
/*
* TODO: we should make packaging to ext mapping configurable, since it's not possible to
* cover all cases.
*/
if ("pom".equals(packaging)) {
// no artifact defined! Add the default artifact if it exist.
DependencyResolver resolver = parserSettings.getResolver(mrid);
if (resolver != null) {
DefaultArtifact artifact = new DefaultArtifact(
mrid, new Date(), artifactId, "jar", "jar");
ArtifactOrigin artifactOrigin = resolver.locate(artifact);
if (!ArtifactOrigin.isUnknown(artifactOrigin)) {
mainArtifact = artifact;
ivyModuleDescriptor.addArtifact("master", mainArtifact);
}
}
return;
} else if (JAR_PACKAGINGS.contains(packaging)) {
ext = "jar";
} else {
ext = packaging;
}
mainArtifact = new DefaultArtifact(mrid, new Date(), artifactId, packaging, ext);
ivyModuleDescriptor.addArtifact("master", mainArtifact);
}
public void addDependency(Resource res, PomDependencyData dep) {
String scope = dep.getScope();
if ((scope != null) && (scope.length() > 0) && !MAVEN2_CONF_MAPPING.containsKey(scope)) {
// unknown scope, defaulting to 'compile'
scope = "compile";
}
String version = dep.getVersion();
version = (version == null || version.length() == 0) ? getDefaultVersion(dep) : version;
ModuleRevisionId moduleRevId = ModuleRevisionId.newInstance(dep.getGroupId(), dep
.getArtifactId(), version);
DefaultDependencyDescriptor dd = new DefaultDependencyDescriptor(ivyModuleDescriptor,
moduleRevId, true, false, true);
scope = (scope == null || scope.length() == 0) ? getDefaultScope(dep) : scope;
ConfMapper mapping = (ConfMapper) MAVEN2_CONF_MAPPING.get(scope);
mapping.addMappingConfs(dd, dep.isOptional());
Map extraAtt = new HashMap();
if ((dep.getClassifier() != null) || (dep.getType() != null)) {
String type = "jar";
if (dep.getType() != null) {
type = dep.getType();
}
// we deal with classifiers by setting an extra attribute and forcing the
// dependency to assume such an artifact is published
if (dep.getClassifier() != null) {
extraAtt.put("m:classifier", dep.getClassifier());
}
DefaultDependencyArtifactDescriptor depArtifact =
new DefaultDependencyArtifactDescriptor(dd, dd.getDependencyId().getName(),
type, type, null, extraAtt);
// here we have to assume a type and ext for the artifact, so this is a limitation
// compared to how m2 behave with classifiers
String optionalizedScope = dep.isOptional() ? "optional" : scope;
dd.addDependencyArtifact(optionalizedScope, depArtifact);
}
for (Iterator itExcl = dep.getExcludedModules().iterator(); itExcl.hasNext();) {
ModuleId excludedModule = (ModuleId) itExcl.next();
String[] confs = dd.getModuleConfigurations();
for (int k = 0; k < confs.length; k++) {
dd.addExcludeRule(confs[k], new DefaultExcludeRule(new ArtifactId(
excludedModule, PatternMatcher.ANY_EXPRESSION,
PatternMatcher.ANY_EXPRESSION,
PatternMatcher.ANY_EXPRESSION),
ExactPatternMatcher.INSTANCE, null));
}
}
ivyModuleDescriptor.addDependency(dd);
}
public void addDependency(DependencyDescriptor descriptor) {
ivyModuleDescriptor.addDependency(descriptor);
}
public void addDependencyMgt(PomDependencyMgt dep) {
String key = getDependencyMgtExtraInfoKeyForVersion(dep.getGroupId(), dep.getArtifactId());
ivyModuleDescriptor.addExtraInfo(key, dep.getVersion());
if (dep.getScope() != null) {
String scopeKey = getDependencyMgtExtraInfoKeyForScope(
dep.getGroupId(), dep.getArtifactId());
ivyModuleDescriptor.addExtraInfo(scopeKey, dep.getScope());
}
// dependency management info is also used for version mediation of transitive dependencies
ivyModuleDescriptor.addDependencyDescriptorMediator(
ModuleId.newInstance(dep.getGroupId(), dep.getArtifactId()),
ExactPatternMatcher.INSTANCE,
new OverrideDependencyDescriptorMediator(null, dep.getVersion()));
}
public void addPlugin(PomDependencyMgt plugin) {
String pluginValue = plugin.getGroupId() + EXTRA_INFO_DELIMITER + plugin.getArtifactId()
+ EXTRA_INFO_DELIMITER + plugin.getVersion();
String pluginExtraInfo = (String) ivyModuleDescriptor.getExtraInfo().get("m:maven.plugins");
if (pluginExtraInfo == null) {
pluginExtraInfo = pluginValue;
} else {
pluginExtraInfo = pluginExtraInfo + "|" + pluginValue;
}
ivyModuleDescriptor.getExtraInfo().put("m:maven.plugins", pluginExtraInfo);
}
public static List /*<PomDependencyMgt>*/ getPlugins(ModuleDescriptor md) {
List result = new ArrayList();
String plugins = (String) md.getExtraInfo().get("m:maven.plugins");
if (plugins == null) {
return new ArrayList();
}
String[] pluginsArray = plugins.split("\\|");
for (int i = 0; i < pluginsArray.length; i++) {
String[] parts = pluginsArray[i].split(EXTRA_INFO_DELIMITER);
result.add(new PomPluginElement(parts[0], parts[1], parts[2]));
}
return result;
}
private static class PomPluginElement implements PomDependencyMgt {
private String groupId;
private String artifactId;
private String version;
public PomPluginElement(String groupId, String artifactId, String version) {
this.groupId = groupId;
this.artifactId = artifactId;
this.version = version;
}
public String getGroupId() {
return groupId;
}
public String getArtifactId() {
return artifactId;
}
public String getVersion() {
return version;
}
public String getScope() {
return null;
}
}
private String getDefaultVersion(PomDependencyData dep) {
String key = getDependencyMgtExtraInfoKeyForVersion(dep.getGroupId(), dep.getArtifactId());
return (String) ivyModuleDescriptor.getExtraInfo().get(key);
}
private String getDefaultScope(PomDependencyData dep) {
String key = getDependencyMgtExtraInfoKeyForScope(dep.getGroupId(), dep.getArtifactId());
String result = (String) ivyModuleDescriptor.getExtraInfo().get(key);
if ((result == null) || !MAVEN2_CONF_MAPPING.containsKey(result)) {
result = "compile";
}
return result;
}
private static String getDependencyMgtExtraInfoKeyForVersion(
String groupId, String artifaceId) {
return DEPENDENCY_MANAGEMENT + EXTRA_INFO_DELIMITER + groupId
+ EXTRA_INFO_DELIMITER + artifaceId + EXTRA_INFO_DELIMITER + "version";
}
private static String getDependencyMgtExtraInfoKeyForScope(String groupId, String artifaceId) {
return DEPENDENCY_MANAGEMENT + EXTRA_INFO_DELIMITER + groupId
+ EXTRA_INFO_DELIMITER + artifaceId + EXTRA_INFO_DELIMITER + "scope";
}
private static String getPropertyExtraInfoKey(String propertyName) {
return PROPERTIES + EXTRA_INFO_DELIMITER + propertyName;
}
public static Map/*<ModuleId, String version>*/
getDependencyManagementMap(ModuleDescriptor md) {
Map ret = new LinkedHashMap();
for (Iterator iterator = md.getExtraInfo().entrySet().iterator(); iterator.hasNext();) {
Map.Entry entry = (Map.Entry) iterator.next();
String key = (String) entry.getKey();
if ((key).startsWith(DEPENDENCY_MANAGEMENT)) {
String[] parts = key.split(EXTRA_INFO_DELIMITER);
if (parts.length != DEPENDENCY_MANAGEMENT_KEY_PARTS_COUNT) {
Message.warn("what seem to be a dependency management extra info "
+ "doesn't match expected pattern: " + key);
} else {
ret.put(ModuleId.newInstance(parts[1], parts[2]), entry.getValue());
}
}
}
return ret;
}
public static List getDependencyManagements(ModuleDescriptor md) {
List result = new ArrayList();
for (Iterator iterator = md.getExtraInfo().entrySet().iterator(); iterator.hasNext();) {
Map.Entry entry = (Map.Entry) iterator.next();
String key = (String) entry.getKey();
if ((key).startsWith(DEPENDENCY_MANAGEMENT)) {
String[] parts = key.split(EXTRA_INFO_DELIMITER);
if (parts.length != DEPENDENCY_MANAGEMENT_KEY_PARTS_COUNT) {
Message.warn("what seem to be a dependency management extra info "
+ "doesn't match expected pattern: " + key);
} else {
String versionKey = DEPENDENCY_MANAGEMENT + EXTRA_INFO_DELIMITER + parts[1]
+ EXTRA_INFO_DELIMITER + parts[2]
+ EXTRA_INFO_DELIMITER + "version";
String scopeKey = DEPENDENCY_MANAGEMENT + EXTRA_INFO_DELIMITER + parts[1]
+ EXTRA_INFO_DELIMITER + parts[2]
+ EXTRA_INFO_DELIMITER + "scope";
String version = (String) md.getExtraInfo().get(versionKey);
String scope = (String) md.getExtraInfo().get(scopeKey);
result.add(new DefaultPomDependencyMgt(parts[1], parts[2], version, scope));
}
}
}
return result;
}
public void addExtraInfos(Map extraAttributes) {
for (Iterator it = extraAttributes.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Entry) it.next();
String key = (String) entry.getKey();
String value = (String) entry.getValue();
addExtraInfo(key, value);
}
}
private void addExtraInfo(String key, String value) {
if (!ivyModuleDescriptor.getExtraInfo().containsKey(key)) {
ivyModuleDescriptor.addExtraInfo(key, value);
}
}
public static Map extractPomProperties(Map extraInfo) {
Map r = new HashMap();
for (Iterator it = extraInfo.entrySet().iterator(); it.hasNext();) {
Map.Entry extraInfoEntry = (Map.Entry) it.next();
if (((String) extraInfoEntry.getKey()).startsWith(PROPERTIES)) {
String prop = ((String) extraInfoEntry.getKey()).substring(PROPERTIES.length()
+ EXTRA_INFO_DELIMITER.length());
r.put(prop, extraInfoEntry.getValue());
}
}
return r;
}
public void addProperty(String propertyName, String value) {
addExtraInfo(getPropertyExtraInfoKey(propertyName), value);
}
public Artifact getMainArtifact() {
return mainArtifact;
}
public Artifact getSourceArtifact() {
return new MDArtifact(
ivyModuleDescriptor, mrid.getName(), "source", "jar",
null, Collections.singletonMap("m:classifier", "sources"));
}
public Artifact getJavadocArtifact() {
return new MDArtifact(
ivyModuleDescriptor, mrid.getName(), "javadoc", "jar",
null, Collections.singletonMap("m:classifier", "javadoc"));
}
public void addSourceArtifact() {
ivyModuleDescriptor.addArtifact("sources", getSourceArtifact());
}
public void addJavadocArtifact() {
ivyModuleDescriptor.addArtifact("javadoc", getJavadocArtifact());
}
}