blob: 11ca95365d79dfa102765c89605fb9a41b39095f [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.core.module.descriptor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
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.MatcherHelper;
import org.apache.ivy.plugins.namespace.NameSpaceHelper;
import org.apache.ivy.plugins.namespace.Namespace;
import org.apache.ivy.plugins.namespace.NamespaceTransformer;
import org.apache.ivy.util.Checks;
/**
* This class can be used as the default implementation for DependencyDescriptor. It implements
* required methods and enables to fill dependency information with the addDependencyConfiguration
* method.
*/
public class DefaultDependencyDescriptor implements DependencyDescriptor {
private static final Pattern SELF_FALLBACK_PATTERN = Pattern
.compile("@(\\+[^\\(]+)?(\\(.*\\))?");
private static final Pattern THIS_FALLBACK_PATTERN = Pattern
.compile("#(\\+[^\\(]+)?(\\(.*\\))?");
/**
* Transforms the given dependency descriptor of the given namespace and return a new dependency
* descriptor in the system namespace. <i>Note that exclude rules are not converted in system
* namespace, because they aren't transformable (the name space hasn't the ability to convert
* regular expressions). However, method doesExclude will work with system artifacts.</i>
*
* @param dd
* @param ns
* @return
*/
public static DependencyDescriptor transformInstance(DependencyDescriptor dd, Namespace ns) {
NamespaceTransformer t = ns.getToSystemTransformer();
if (t.isIdentity()) {
return dd;
}
DefaultDependencyDescriptor newdd = transformInstance(dd, t, false);
newdd.namespace = ns;
return newdd;
}
/**
* Transforms a dependency descriptor using the given transformer. Note that no namespace info
* will be attached to the transformed dependency descriptor, so calling doesExclude is not
* recommended (doesExclude only works when namespace is properly set)
*
* @param dd
* @param t
* @return
*/
public static DefaultDependencyDescriptor transformInstance(DependencyDescriptor dd,
NamespaceTransformer t, boolean fromSystem) {
ModuleRevisionId transformParentId = t.transform(dd.getParentRevisionId());
ModuleRevisionId transformMrid = t.transform(dd.getDependencyRevisionId());
ModuleRevisionId transformDynamicMrid = t.transform(dd
.getDynamicConstraintDependencyRevisionId());
DefaultDependencyDescriptor newdd = new DefaultDependencyDescriptor(null, transformMrid,
transformDynamicMrid, dd.isForce(), dd.isChanging(), dd.isTransitive());
newdd.parentId = transformParentId;
ModuleRevisionId sourceModule = dd.getSourceModule();
if (sourceModule != null) {
newdd.sourceModule = t.transform(sourceModule);
}
String[] moduleConfs = dd.getModuleConfigurations();
if (moduleConfs.length == 1 && "*".equals(moduleConfs[0])) {
if (dd instanceof DefaultDependencyDescriptor) {
DefaultDependencyDescriptor ddd = (DefaultDependencyDescriptor) dd;
newdd.confs = new LinkedHashMap(ddd.confs);
newdd.setExcludeRules(new LinkedHashMap(ddd.getExcludeRules()));
newdd.setIncludeRules(new LinkedHashMap(ddd.getIncludeRules()));
newdd.setDependencyArtifacts(new LinkedHashMap(ddd.getDependencyArtifacts()));
} else {
throw new IllegalArgumentException(
"dependency descriptor transformation does not support * module confs "
+ "with descriptors which aren't DefaultDependencyDescriptor");
}
} else {
for (int i = 0; i < moduleConfs.length; i++) {
newdd.confs.put(moduleConfs[i],
new ArrayList(Arrays.asList(dd.getDependencyConfigurations(moduleConfs[i]))));
newdd.getExcludeRules().put(moduleConfs[i],
new ArrayList(Arrays.asList(dd.getExcludeRules(moduleConfs[i]))));
newdd.getIncludeRules().put(moduleConfs[i],
new ArrayList(Arrays.asList(dd.getIncludeRules(moduleConfs[i]))));
newdd.getDependencyArtifacts().put(moduleConfs[i],
new ArrayList(Arrays.asList(dd.getDependencyArtifacts(moduleConfs[i]))));
}
}
if (fromSystem) {
newdd.asSystem = dd;
}
return newdd;
}
private final ModuleRevisionId revId;
private ModuleRevisionId dynamicRevId;
private Map/* <String,List<String>> */confs = new LinkedHashMap();
// Map (String masterConf -> Collection(DependencyArtifactDescriptor))
private Map dependencyArtifacts; // initialized on demand only for memory consumption reason
// Map (String masterConf -> Collection(IncludeRule))
private Map includeRules; // initialized on demand only for memory consumption reason
// Map (String masterConf -> Collection(ExcludeRule))
private Map excludeRules; // initialized on demand only for memory consumption reason
/**
* Used to indicate that this revision must be used in case of conflicts, independently of
* conflicts manager
*/
private boolean isForce;
/**
* Used to indicate that the dependency is a changing one, i.e. that ivy should not rely on the
* version to know if it can trust artifacts in cache
*/
private boolean isChanging;
private ModuleRevisionId parentId;
private boolean isTransitive = true;
/**
* This namespace should be used to check
*/
private Namespace namespace = null;
private final ModuleDescriptor md;
private DependencyDescriptor asSystem = this;
private ModuleRevisionId sourceModule;
private DefaultDependencyDescriptor(DefaultDependencyDescriptor dd, ModuleRevisionId revision) {
Checks.checkNotNull(dd, "dd");
Checks.checkNotNull(revision, "revision");
if (!revision.getModuleId().equals(dd.getDependencyId())) {
throw new IllegalArgumentException(
"new ModuleRevisionId MUST have the same ModuleId as original one."
+ " original = " + dd.getDependencyId() + " new = "
+ revision.getModuleId());
}
md = dd.md;
parentId = dd.parentId;
revId = revision;
dynamicRevId = dd.dynamicRevId;
isForce = dd.isForce;
isChanging = dd.isChanging;
isTransitive = dd.isTransitive;
namespace = dd.namespace;
confs.putAll(dd.confs);
excludeRules = dd.excludeRules == null ? null : new LinkedHashMap(dd.excludeRules);
includeRules = dd.includeRules == null ? null : new LinkedHashMap(dd.includeRules);
dependencyArtifacts = dd.dependencyArtifacts == null ? null : new LinkedHashMap(
dd.dependencyArtifacts);
sourceModule = dd.sourceModule;
}
public DefaultDependencyDescriptor(ModuleDescriptor md, ModuleRevisionId mrid, boolean force,
boolean changing, boolean transitive) {
this(md, mrid, mrid, force, changing, transitive);
}
public DefaultDependencyDescriptor(ModuleRevisionId mrid, boolean force) {
this(mrid, force, false);
}
public DefaultDependencyDescriptor(ModuleRevisionId mrid, boolean force, boolean changing) {
this(null, mrid, mrid, force, changing, true);
}
public DefaultDependencyDescriptor(ModuleDescriptor md, ModuleRevisionId mrid,
ModuleRevisionId dynamicConstraint, boolean force, boolean changing, boolean transitive) {
Checks.checkNotNull(mrid, "mrid");
Checks.checkNotNull(dynamicConstraint, "dynamicConstraint");
this.md = md;
revId = mrid;
dynamicRevId = dynamicConstraint;
isForce = force;
isChanging = changing;
isTransitive = transitive;
sourceModule = md == null ? null : md.getModuleRevisionId();
}
public ModuleId getDependencyId() {
return getDependencyRevisionId().getModuleId();
}
public ModuleRevisionId getDependencyRevisionId() {
return revId;
}
public ModuleRevisionId getDynamicConstraintDependencyRevisionId() {
return dynamicRevId;
}
public String[] getModuleConfigurations() {
return (String[]) confs.keySet().toArray(new String[confs.keySet().size()]);
}
public String[] getDependencyConfigurations(String moduleConfiguration) {
return getDependencyConfigurations(moduleConfiguration, moduleConfiguration);
}
/**
* Return the dependency configurations mapped to the given moduleConfiguration, actually
* resolved because of the given requestedConfiguration
* <p>
* Usually requestedConfiguration and moduleConfiguration are the same, except when a conf
* extends another, then the moduleConfiguration is the configuration currently resolved (the
* extended one), and requestedConfiguration is the one actually requested initially (the
* extending one). Both moduleConfiguration and requestedConfiguration are configurations of the
* caller, the array returned is composed of the required configurations of the dependency
* described by this descriptor.
*/
public String[] getDependencyConfigurations(String moduleConfiguration,
String requestedConfiguration) {
if (md != null) {
Configuration c = md.getConfiguration(moduleConfiguration);
if (c instanceof ConfigurationIntersection) {
ConfigurationIntersection intersection = (ConfigurationIntersection) c;
Set /* <String> */intersectedDepConfs = new HashSet();
String[] intersected = intersection.getIntersectedConfigurationNames();
for (int i = 0; i < intersected.length; i++) {
Collection depConfs = getDependencyConfigurationsIncludingExtending(
intersected[i], requestedConfiguration);
if (intersectedDepConfs.isEmpty()) {
intersectedDepConfs.addAll(depConfs);
} else {
if (intersectedDepConfs.contains("*")) {
intersectedDepConfs.remove("*");
intersectedDepConfs.addAll(depConfs);
} else if (depConfs.contains("*")) {
// nothing to do, intersection of 'something'
// with 'everything' is 'something'
} else {
Set /* <String> */intersectedDepConfsCopy = intersectedDepConfs;
intersectedDepConfs = new HashSet();
for (Iterator it = intersectedDepConfsCopy.iterator(); it.hasNext();) {
String intersectedDepConf = (String) it.next();
if (depConfs.contains(intersectedDepConf)) {
// the conf is present in both sets,
// so it is in the intersection
intersectedDepConfs.add(intersectedDepConf);
continue;
}
/*
* we do not handle special confs like *!sg or [cond]* in right hand
* confs yet: it would require supporting parenthesis grouping in
* configurations intersection interpretation
*
* for (Iterator it2 = depConfs.iterator(); it2.hasNext();) { String
* depConf = (String) it2.next(); if (depConf.startsWith("*")) { if
* (intersectedDepConf .indexOf("(" + depConf + ")") != -1) {
* intersectedDepConfs.add(intersectedDepConf); } else {
* intersectedDepConfs.add( "(" + intersectedDepConf + ")+(" +
* depConf + ")"); } } else if (intersectedDepConf.startsWith("*"))
* { if (depConf .indexOf("(" + intersectedDepConf + ")") != -1) {
* intersectedDepConfs.add(depConf); } else {
* intersectedDepConfs.add( depConf + "+" + intersectedDepConf); } }
* }
*/
}
}
}
}
List confsList = (List) confs.get(moduleConfiguration);
if (confsList != null) {
intersectedDepConfs.addAll(confsList);
}
if (intersectedDepConfs.isEmpty()) {
List defConfs = (List) confs.get("*");
if (defConfs != null) {
for (Iterator it = defConfs.iterator(); it.hasNext();) {
String mappedConf = (String) it.next();
if (mappedConf != null && mappedConf.startsWith("@+")) {
return new String[] {moduleConfiguration + mappedConf.substring(1)};
} else if (mappedConf != null && mappedConf.equals("@")) {
return new String[] {moduleConfiguration};
}
}
}
}
return (String[]) intersectedDepConfs
.toArray(new String[intersectedDepConfs.size()]);
} else if (c instanceof ConfigurationGroup) {
ConfigurationGroup group = (ConfigurationGroup) c;
Set /* <String> */groupDepConfs = new HashSet();
String[] members = group.getMembersConfigurationNames();
for (int i = 0; i < members.length; i++) {
Collection depConfs = getDependencyConfigurationsIncludingExtending(members[i],
requestedConfiguration);
groupDepConfs.addAll(depConfs);
}
return (String[]) groupDepConfs.toArray(new String[groupDepConfs.size()]);
}
}
List confsList = (List) confs.get(moduleConfiguration);
if (confsList == null) {
// there is no mapping defined for this configuration, add the 'other' mappings.
confsList = (List) confs.get("%");
}
List defConfs = (List) confs.get("*");
Collection ret = new LinkedHashSet();
if (confsList != null) {
ret.addAll(confsList);
}
if (defConfs != null) {
ret.addAll(defConfs);
}
Collection replacedRet = new LinkedHashSet();
for (Iterator iter = ret.iterator(); iter.hasNext();) {
String c = (String) iter.next();
String replacedConf = replaceSelfFallbackPattern(c, moduleConfiguration);
if (replacedConf == null) {
replacedConf = replaceThisFallbackPattern(c, requestedConfiguration);
}
if (replacedConf != null) {
c = replacedConf;
}
replacedRet.add(c);
}
ret = replacedRet;
if (ret.remove("*")) {
StringBuffer r = new StringBuffer("*");
// merge excluded configurations as one conf like *!A!B
for (Iterator iter = ret.iterator(); iter.hasNext();) {
String c = (String) iter.next();
if (c.startsWith("!")) {
r.append(c);
}
}
return new String[] {r.toString()};
}
return (String[]) ret.toArray(new String[ret.size()]);
}
private Collection getDependencyConfigurationsIncludingExtending(String conf,
String requestedConfiguration) {
Set/* <String> */allDepConfs = new LinkedHashSet();
allDepConfs
.addAll(Arrays.asList(getDependencyConfigurations(conf, requestedConfiguration)));
Collection extendingConfs = Configuration.findConfigurationExtending(conf,
md.getConfigurations());
for (Iterator it = extendingConfs.iterator(); it.hasNext();) {
Configuration extendingConf = (Configuration) it.next();
allDepConfs.addAll(Arrays.asList(getDependencyConfigurations(extendingConf.getName(),
requestedConfiguration)));
}
return allDepConfs;
}
protected static String replaceSelfFallbackPattern(final String conf,
final String moduleConfiguration) {
return replaceFallbackConfigurationPattern(SELF_FALLBACK_PATTERN, conf, moduleConfiguration);
}
protected static String replaceThisFallbackPattern(final String conf,
final String requestedConfiguration) {
return replaceFallbackConfigurationPattern(THIS_FALLBACK_PATTERN, conf,
requestedConfiguration);
}
/**
* Replaces fallback patterns with correct values if fallback pattern exists.
*
* @param pattern
* pattern to look for
* @param conf
* configuration mapping from dependency element
* @param moduleConfiguration
* module's configuration to use for replacement
* @return Replaced string if pattern matched. Otherwise null.
*/
protected static String replaceFallbackConfigurationPattern(final Pattern pattern,
final String conf, final String moduleConfiguration) {
Matcher matcher = pattern.matcher(conf);
if (matcher.matches()) {
String mappedConf = moduleConfiguration;
if (matcher.group(1) != null) {
mappedConf = mappedConf + matcher.group(1);
}
if (matcher.group(2) != null) {
mappedConf = mappedConf + matcher.group(2);
}
return mappedConf;
}
return null;
}
public String[] getDependencyConfigurations(String[] moduleConfigurations) {
Set confs = new LinkedHashSet();
for (int i = 0; i < moduleConfigurations.length; i++) {
confs.addAll(Arrays.asList(getDependencyConfigurations(moduleConfigurations[i])));
}
if (confs.contains("*")) {
return new String[] {"*"};
}
return (String[]) confs.toArray(new String[confs.size()]);
}
public DependencyArtifactDescriptor[] getDependencyArtifacts(String moduleConfiguration) {
Collection artifacts = getCollectionForConfiguration(moduleConfiguration,
dependencyArtifacts);
return (DependencyArtifactDescriptor[]) artifacts
.toArray(new DependencyArtifactDescriptor[artifacts.size()]);
}
public IncludeRule[] getIncludeRules(String moduleConfiguration) {
Collection rules = getCollectionForConfiguration(moduleConfiguration, includeRules);
return (IncludeRule[]) rules.toArray(new IncludeRule[rules.size()]);
}
public ExcludeRule[] getExcludeRules(String moduleConfiguration) {
Collection rules = getCollectionForConfiguration(moduleConfiguration, excludeRules);
return (ExcludeRule[]) rules.toArray(new ExcludeRule[rules.size()]);
}
private Set getCollectionForConfiguration(String moduleConfiguration, Map collectionMap) {
if (collectionMap == null || collectionMap.isEmpty()) {
return Collections.EMPTY_SET;
}
Collection artifacts = (Collection) collectionMap.get(moduleConfiguration);
Collection defArtifacts = (Collection) collectionMap.get("*");
Set ret = new LinkedHashSet();
if (artifacts != null) {
ret.addAll(artifacts);
}
if (defArtifacts != null) {
ret.addAll(defArtifacts);
}
return ret;
}
public DependencyArtifactDescriptor[] getDependencyArtifacts(String[] moduleConfigurations) {
Set artifacts = new LinkedHashSet();
for (int i = 0; i < moduleConfigurations.length; i++) {
artifacts.addAll(Arrays.asList(getDependencyArtifacts(moduleConfigurations[i])));
}
return (DependencyArtifactDescriptor[]) artifacts
.toArray(new DependencyArtifactDescriptor[artifacts.size()]);
}
public IncludeRule[] getIncludeRules(String[] moduleConfigurations) {
Set rules = new LinkedHashSet();
for (int i = 0; i < moduleConfigurations.length; i++) {
rules.addAll(Arrays.asList(getIncludeRules(moduleConfigurations[i])));
}
return (IncludeRule[]) rules.toArray(new IncludeRule[rules.size()]);
}
public ExcludeRule[] getExcludeRules(String[] moduleConfigurations) {
Set rules = new LinkedHashSet();
for (int i = 0; i < moduleConfigurations.length; i++) {
rules.addAll(Arrays.asList(getExcludeRules(moduleConfigurations[i])));
}
return (ExcludeRule[]) rules.toArray(new ExcludeRule[rules.size()]);
}
public DependencyArtifactDescriptor[] getAllDependencyArtifacts() {
if (dependencyArtifacts == null) {
return new DependencyArtifactDescriptor[0];
}
Set ret = mergeAll(dependencyArtifacts);
return (DependencyArtifactDescriptor[]) ret.toArray(new DependencyArtifactDescriptor[ret
.size()]);
}
public IncludeRule[] getAllIncludeRules() {
if (includeRules == null) {
return new IncludeRule[0];
}
Set ret = mergeAll(includeRules);
return (IncludeRule[]) ret.toArray(new IncludeRule[ret.size()]);
}
public ExcludeRule[] getAllExcludeRules() {
if (excludeRules == null) {
return new ExcludeRule[0];
}
Set ret = mergeAll(excludeRules);
return (ExcludeRule[]) ret.toArray(new ExcludeRule[ret.size()]);
}
private Set mergeAll(Map artifactsMap) {
Set ret = new LinkedHashSet();
for (Iterator it = artifactsMap.values().iterator(); it.hasNext();) {
Collection artifacts = (Collection) it.next();
ret.addAll(artifacts);
}
return ret;
}
public void addDependencyConfiguration(String masterConf, String depConf) {
if ((md != null) && !"*".equals(masterConf) && !"%".equals(masterConf)) {
Configuration config;
if (masterConf.startsWith("!")) {
config = md.getConfiguration(masterConf.substring(1));
} else {
config = md.getConfiguration(masterConf);
}
if (config == null) {
throw new IllegalArgumentException("Cannot add dependency '" + revId
+ "' to configuration '" + masterConf + "' of module "
+ md.getModuleRevisionId() + " because this configuration doesn't exist!");
}
if (config instanceof ConfigurationGroup) {
ConfigurationGroup group = (ConfigurationGroup) config;
String[] members = group.getMembersConfigurationNames();
for (int i = 0; i < members.length; i++) {
addDependencyConfiguration(members[i], depConf);
}
return;
}
}
List confsList = (List) confs.get(masterConf);
if (confsList == null) {
confsList = new ArrayList();
confs.put(masterConf, confsList);
}
if (!confsList.contains(depConf)) {
confsList.add(depConf);
}
}
public void addDependencyArtifact(String masterConf, DependencyArtifactDescriptor dad) {
addObjectToConfiguration(masterConf, dad, getDependencyArtifacts());
}
public void addIncludeRule(String masterConf, IncludeRule rule) {
addObjectToConfiguration(masterConf, rule, getIncludeRules());
}
public void addExcludeRule(String masterConf, ExcludeRule rule) {
addObjectToConfiguration(masterConf, rule, getExcludeRules());
}
private void addObjectToConfiguration(String callerConf, Object toAdd, Map confsMap) {
Collection col = (Collection) confsMap.get(callerConf);
if (col == null) {
col = new ArrayList();
confsMap.put(callerConf, col);
}
col.add(toAdd);
}
/**
* only works when namespace is properly set. The behaviour is not specified if namespace is not
* set
*/
public boolean doesExclude(String[] moduleConfigurations, ArtifactId artifactId) {
if (namespace != null) {
artifactId = NameSpaceHelper
.transform(artifactId, namespace.getFromSystemTransformer());
}
ExcludeRule[] rules = getExcludeRules(moduleConfigurations);
for (int i = 0; i < rules.length; i++) {
if (MatcherHelper.matches(rules[i].getMatcher(), rules[i].getId(), artifactId)) {
return true;
}
}
return false;
}
/**
* Returns true if this descriptor contains any exclusion rule
*
* @return
*/
public boolean canExclude() {
return excludeRules != null && !excludeRules.isEmpty();
}
public String toString() {
return "dependency: " + revId + " " + confs;
}
public boolean isForce() {
return isForce;
}
public ModuleRevisionId getParentRevisionId() {
return md != null ? md.getResolvedModuleRevisionId() : parentId;
}
public boolean isChanging() {
return isChanging;
}
public boolean isTransitive() {
return isTransitive;
}
public Namespace getNamespace() {
return namespace;
}
public String getAttribute(String attName) {
return revId.getAttribute(attName);
}
public Map getAttributes() {
return revId.getAttributes();
}
public String getExtraAttribute(String attName) {
return revId.getExtraAttribute(attName);
}
public Map getExtraAttributes() {
return revId.getExtraAttributes();
}
public Map getQualifiedExtraAttributes() {
return revId.getQualifiedExtraAttributes();
}
public DependencyDescriptor asSystem() {
return asSystem;
}
private void setDependencyArtifacts(Map dependencyArtifacts) {
this.dependencyArtifacts = dependencyArtifacts;
}
private Map getDependencyArtifacts() {
if (dependencyArtifacts == null) {
dependencyArtifacts = new LinkedHashMap();
}
return dependencyArtifacts;
}
private void setIncludeRules(Map includeRules) {
this.includeRules = includeRules;
}
private Map getIncludeRules() {
if (includeRules == null) {
includeRules = new LinkedHashMap();
}
return includeRules;
}
private void setExcludeRules(Map excludeRules) {
this.excludeRules = excludeRules;
}
private Map getExcludeRules() {
if (excludeRules == null) {
excludeRules = new LinkedHashMap();
}
return excludeRules;
}
public ModuleRevisionId getSourceModule() {
return sourceModule;
}
public DependencyDescriptor clone(ModuleRevisionId revision) {
return new DefaultDependencyDescriptor(this, revision);
}
}