Index: doc/toc.json
===================================================================
--- doc/toc.json (revision 920176)
+++ doc/toc.json (working copy)
@@ -424,6 +424,13 @@
"title":"info",
"children": [
{
+ "id":"ivyfile/extends",
+ "title":"extends",
+ "children": [
+
+ ]
+ },
+ {
"id":"ivyfile/license",
"title":"license",
"children": [
Index: doc/ivyfile/extends.html
===================================================================
--- doc/ivyfile/extends.html (revision 0)
+++ doc/ivyfile/extends.html (revision 0)
@@ -0,0 +1,66 @@
+
+
+
+
+
+ | extends | identifies a parent Ivy file from which this descriptor inherits content |
+ 0..n |
| license | contains information about the licenses of the described module |
0..n |
| ivyauthor | describes who has contributed to write the ivy file |
Index: doc/use/deliver.html
===================================================================
--- doc/use/deliver.html (revision 920176)
+++ doc/use/deliver.html (working copy)
@@ -99,6 +99,8 @@
No. Defaults to default ivy value (as configured in configuration file) |
| replacedynamicrev | true to replace dynmic revisions by static ones in the delivered file, false to avoid this replacement (since 1.3) |
No. Defaults to true |
+ | merge | if a descriptor extends a parent, merge the inherited information directly into the delivered descriptor. The extends element itself will be commented out in the delivered descriptor. |
+ No. Defaults to false. |
| settingsRef | A reference to the ivy settings that must be used by this task (since 2.0) |
No, 'ivy.instance' is taken by default. |
| conf | comma-separated list of configurations to include in the delivered file. Accepts wildcards. (since 2.0) |
Index: doc/use/publish.html
===================================================================
--- doc/use/publish.html (revision 920176)
+++ doc/use/publish.html (working copy)
@@ -60,6 +60,8 @@
No. Defaults to false |
| update | true to update ivy file metadata (revision, branch, publication date and status) before publishing, false otherwise. This is usually not necessary when using deliver before publish. |
No. Defaults to false |
+ | merge | if this descriptor extends a parent, merge the inherited information directly into this descriptor on publish. The extends element itself will be commented out in the published descriptor. |
+ No. Defaults to the value of update. |
| validate | true to force ivy files validation against ivy.xsd, false to force no validation |
No. Defaults to default ivy value (as configured in [[settings settings file]]) |
| replacedynamicrev | true to replace dynmic revisions by static ones in the delivered file, false to avoid this replacement since 1.3 |
Index: src/java/org/apache/ivy/ant/IvyDeliver.java
===================================================================
--- src/java/org/apache/ivy/ant/IvyDeliver.java (revision 920176)
+++ src/java/org/apache/ivy/ant/IvyDeliver.java (working copy)
@@ -206,6 +206,8 @@
private String pubBranch;
private boolean generateRevConstraint = true;
+
+ private boolean merge;
public void setCache(File cache) {
cacheAttributeNotSupported();
@@ -315,6 +317,14 @@
this.generateRevConstraint = generateRevConstraint;
}
+ public boolean isMerge() {
+ return merge;
+ }
+
+ public void setMerge(boolean merge) {
+ this.merge = merge;
+ }
+
public void doExecute() throws BuildException {
Ivy ivy = getIvyInstance();
IvySettings settings = ivy.getSettings();
@@ -394,6 +404,7 @@
drResolver, doValidate(settings), replacedynamicrev, splitConfs(conf))
.setResolveId(resolveId)
.setGenerateRevConstraint(generateRevConstraint)
+ .setMerge(merge)
.setPubBranch(pubBranch);
if (mrid == null) {
ivy.deliver(pubRevision, deliverpattern, options);
Index: src/java/org/apache/ivy/ant/IvyPublish.java
===================================================================
--- src/java/org/apache/ivy/ant/IvyPublish.java (revision 920176)
+++ src/java/org/apache/ivy/ant/IvyPublish.java (working copy)
@@ -72,6 +72,8 @@
private boolean overwrite = false;
private boolean update = false;
+
+ private Boolean merge = null;
private boolean replacedynamicrev = true;
@@ -208,6 +210,14 @@
this.replacedynamicrev = replacedynamicrev;
}
+ public Boolean getMerge() {
+ return merge;
+ }
+
+ public void setMerge(Boolean merge) {
+ this.merge = merge;
+ }
+
public void doExecute() throws BuildException {
Ivy ivy = getIvyInstance();
IvySettings settings = ivy.getSettings();
@@ -291,6 +301,9 @@
deliver.setStatus(getStatus());
deliver.setValidate(doValidate(settings));
deliver.setReplacedynamicrev(isReplacedynamicrev());
+ if (merge != null) {
+ deliver.setMerge(merge.booleanValue());
+ }
deliver.setConf(conf);
deliver.execute();
@@ -308,6 +321,7 @@
.setValidate(doValidate(settings))
.setOverwrite(overwrite)
.setUpdate(update)
+ .setMerge(merge)
.setWarnOnMissing(warnonmissing)
.setHaltOnMissing(haltonmissing)
.setConfs(splitConfs(conf)));
Index: src/java/org/apache/ivy/core/deliver/DeliverEngine.java
===================================================================
--- src/java/org/apache/ivy/core/deliver/DeliverEngine.java (revision 920176)
+++ src/java/org/apache/ivy/core/deliver/DeliverEngine.java (working copy)
@@ -192,6 +192,8 @@
.setBranch(options.getPubBranch())
.setPubdate(options.getPubdate())
.setGenerateRevConstraint(options.isGenerateRevConstraint())
+ .setMerge(options.isMerge())
+ .setMergedDescriptor(md)
.setConfsToExclude((String[]) confsToRemove
.toArray(new String[confsToRemove.size()])));
} catch (SAXException ex) {
Index: src/java/org/apache/ivy/core/deliver/DeliverOptions.java
===================================================================
--- src/java/org/apache/ivy/core/deliver/DeliverOptions.java (revision 920176)
+++ src/java/org/apache/ivy/core/deliver/DeliverOptions.java (working copy)
@@ -46,7 +46,9 @@
* applicable, false to never generate the revConstraint attribute.
*/
private boolean generateRevConstraint = true;
-
+
+ /** true to merge parent descriptor elements into delivered child descriptor */
+ private boolean merge = false;
/**
* Returns an instance of DeliverOptions with options corresponding to default values taken from
@@ -237,7 +239,15 @@
return this;
}
-
+ public boolean isMerge() {
+ return merge;
+ }
+
+ public DeliverOptions setMerge(boolean merge) {
+ this.merge = merge;
+ return this;
+ }
+
public String toString() {
return "status=" + status + " pubdate=" + pubdate + " validate=" + validate
+ " resolveDynamicRevisions=" + resolveDynamicRevisions
Index: src/java/org/apache/ivy/core/module/descriptor/Configuration.java
===================================================================
--- src/java/org/apache/ivy/core/module/descriptor/Configuration.java (revision 920176)
+++ src/java/org/apache/ivy/core/module/descriptor/Configuration.java (working copy)
@@ -21,14 +21,16 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashSet;
+import java.util.Map;
import java.util.Set;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
import org.apache.ivy.util.extendable.DefaultExtendableItem;
/**
* Represents a module configuration
*/
-public class Configuration extends DefaultExtendableItem {
+public class Configuration extends DefaultExtendableItem implements InheritableItem {
public static final class Visibility {
public static final Visibility PUBLIC = new Visibility("public");
@@ -77,6 +79,8 @@
private boolean transitive = true;
private String deprecated;
+
+ private ModuleRevisionId sourceModule;
/**
* Creates a new configuration.
@@ -86,6 +90,12 @@
public Configuration(String name) {
this(name, Visibility.PUBLIC, null, null, true, null);
}
+
+ public Configuration(Configuration source, ModuleRevisionId sourceModule) {
+ this(source.getAttributes(), source.getQualifiedExtraAttributes(), source.getName(),
+ source.getVisibility(), source.getDescription(), source.getExtends(),
+ source.isTransitive(), source.getDeprecated(), sourceModule);
+ }
/**
* Creates a new configuration.
@@ -99,6 +109,14 @@
*/
public Configuration(String name, Visibility visibility, String description, String[] ext,
boolean transitive, String deprecated) {
+ this(null, null, name, visibility, description, ext, transitive, deprecated, null);
+ }
+
+ private Configuration(Map attributes, Map extraAttributes, String name, Visibility visibility,
+ String description, String[] ext, boolean transitive, String deprecated,
+ ModuleRevisionId sourceModule) {
+ super(attributes, extraAttributes);
+
if (name == null) {
throw new NullPointerException("null configuration name not allowed");
}
@@ -118,6 +136,7 @@
}
this.transitive = transitive;
this.deprecated = deprecated;
+ this.sourceModule = sourceModule;
}
/**
@@ -162,6 +181,10 @@
public final boolean isTransitive() {
return transitive;
}
+
+ public ModuleRevisionId getSourceModule() {
+ return sourceModule;
+ }
public String toString() {
return name;
Index: src/java/org/apache/ivy/core/module/descriptor/DefaultDependencyDescriptor.java
===================================================================
--- src/java/org/apache/ivy/core/module/descriptor/DefaultDependencyDescriptor.java (revision 920176)
+++ src/java/org/apache/ivy/core/module/descriptor/DefaultDependencyDescriptor.java (working copy)
@@ -88,7 +88,13 @@
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) {
@@ -159,7 +165,9 @@
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");
@@ -183,8 +191,9 @@
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) {
@@ -211,6 +220,7 @@
isForce = force;
isChanging = changing;
isTransitive = transitive;
+ sourceModule = md == null ? null : md.getModuleRevisionId();
}
public ModuleId getDependencyId() {
@@ -698,6 +708,10 @@
return excludeRules;
}
+ public ModuleRevisionId getSourceModule() {
+ return sourceModule;
+ }
+
public DependencyDescriptor clone(ModuleRevisionId revision) {
return new DefaultDependencyDescriptor(this, revision);
}
Index: src/java/org/apache/ivy/core/module/descriptor/DefaultExtendsDescriptor.java
===================================================================
--- src/java/org/apache/ivy/core/module/descriptor/DefaultExtendsDescriptor.java (revision 0)
+++ src/java/org/apache/ivy/core/module/descriptor/DefaultExtendsDescriptor.java (revision 0)
@@ -0,0 +1,62 @@
+package org.apache.ivy.core.module.descriptor;
+
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DefaultExtendsDescriptor implements ExtendsDescriptor {
+
+ private ModuleRevisionId parentRevisionId;
+ private ModuleRevisionId resolvedParentRevisionId;
+ private String location;
+ private List extendsTypes;
+
+ public DefaultExtendsDescriptor(ModuleRevisionId parentRevisionId,
+ ModuleRevisionId resolvedParentRevisionId,
+ String location, String[] types) {
+ this.parentRevisionId = parentRevisionId;
+ this.resolvedParentRevisionId = resolvedParentRevisionId;
+ this.location = location;
+ this.extendsTypes = new ArrayList(types.length);
+ for (int i = 0; i < types.length; ++i) {
+ extendsTypes.add(types[i]);
+ }
+ }
+
+ public ModuleRevisionId getParentRevisionId() {
+ return parentRevisionId;
+ }
+
+ public ModuleRevisionId getResolvedParentRevisionId() {
+ return resolvedParentRevisionId;
+ }
+
+ public String getLocation() {
+ return location;
+ }
+
+ public String[] getExtendsTypes() {
+ return (String[])extendsTypes.toArray(new String[extendsTypes.size()]);
+ }
+
+ public boolean isAllInherited() {
+ return extendsTypes.contains("all");
+ }
+
+ public boolean isInfoInherited() {
+ return isAllInherited() || extendsTypes.contains("info");
+ }
+
+ public boolean isDescriptionInherited() {
+ return isAllInherited() || extendsTypes.contains("description");
+ }
+
+ public boolean areConfigurationsInherited() {
+ return isAllInherited() || extendsTypes.contains("configurations");
+ }
+
+ public boolean areDependenciesInherited() {
+ return isAllInherited() || extendsTypes.contains("dependencies");
+ }
+}
Index: src/java/org/apache/ivy/core/module/descriptor/DefaultModuleDescriptor.java
===================================================================
--- src/java/org/apache/ivy/core/module/descriptor/DefaultModuleDescriptor.java (revision 920176)
+++ src/java/org/apache/ivy/core/module/descriptor/DefaultModuleDescriptor.java (working copy)
@@ -148,6 +148,15 @@
nmd.status = md.getStatus();
nmd.publicationDate = md.getPublicationDate();
nmd.resolvedPublicationDate = md.getResolvedPublicationDate();
+
+ ExtendsDescriptor[] ed = md.getInheritedDescriptors();
+ for (int i = 0; i < ed.length; ++i) {
+ ModuleRevisionId mrid = t.transform(ed[i].getParentRevisionId());
+ ModuleRevisionId resolvedMrid = t.transform(ed[i].getResolvedParentRevisionId());
+ nmd.inheritedDescriptors.add(new DefaultExtendsDescriptor(mrid, resolvedMrid,
+ ed[i].getLocation(), ed[i].getExtendsTypes()));
+ }
+
DependencyDescriptor[] dd = md.getDependencies();
for (int i = 0; i < dd.length; i++) {
nmd.dependencies.add(NameSpaceHelper.toSystem(dd[i], ns));
@@ -227,6 +236,8 @@
private List excludeRules = new ArrayList(); // List(ExcludeRule)
private Artifact metadataArtifact;
+
+ private List inheritedDescriptors = new ArrayList(); //List(ExtendsDescriptor)
private Map/**/ extraAttributesNamespaces = new LinkedHashMap();
@@ -322,6 +333,10 @@
this.status = status;
}
+ public void addInheritedDescriptor(ExtendsDescriptor descriptor) {
+ inheritedDescriptors.add(descriptor);
+ }
+
public void addDependency(DependencyDescriptor dependency) {
dependencies.add(dependency);
}
@@ -373,6 +388,11 @@
return status;
}
+ public ExtendsDescriptor[] getInheritedDescriptors() {
+ return (ExtendsDescriptor[])inheritedDescriptors.toArray(
+ new ExtendsDescriptor[inheritedDescriptors.size()]);
+ }
+
public Configuration[] getConfigurations() {
return (Configuration[]) configurations.values().toArray(
new Configuration[configurations.size()]);
Index: src/java/org/apache/ivy/core/module/descriptor/DependencyDescriptor.java
===================================================================
--- src/java/org/apache/ivy/core/module/descriptor/DependencyDescriptor.java (revision 920176)
+++ src/java/org/apache/ivy/core/module/descriptor/DependencyDescriptor.java (working copy)
@@ -40,7 +40,7 @@
* be used instead of the default dependency constraint when performing dependency resolution.
*
*/
-public interface DependencyDescriptor extends ExtendableItem {
+public interface DependencyDescriptor extends ExtendableItem, InheritableItem {
ModuleId getDependencyId();
/**
Index: src/java/org/apache/ivy/core/module/descriptor/ExtendsDescriptor.java
===================================================================
--- src/java/org/apache/ivy/core/module/descriptor/ExtendsDescriptor.java (revision 0)
+++ src/java/org/apache/ivy/core/module/descriptor/ExtendsDescriptor.java (revision 0)
@@ -0,0 +1,46 @@
+package org.apache.ivy.core.module.descriptor;
+
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+
+/**
+ * Describes parent descriptor information for a module descriptor.
+ */
+public interface ExtendsDescriptor {
+
+ /** get the module revision id of the declared parent descriptor */
+ public ModuleRevisionId getParentRevisionId();
+ /**
+ * get the resolved revision id for {@link #getParentRevisionId}, see
+ * {@link org.apache.ivy.core.module.descriptor.ModuleDescriptor#getResolvedModuleRevisionId()} }
+ */
+ public ModuleRevisionId getResolvedParentRevisionId();
+
+ /**
+ * If there is an explicit path to check for the parent descriptor, return it.
+ * Otherwise returns null.
+ */
+ public String getLocation();
+
+ /**
+ * Get the parts of the parent descriptor that are inherited. Default
+ * supported types are info, description,
+ * configurations, dependencies, and/or all.
+ * Ivy extensions may add support for additional extends types.
+ */
+ public String[] getExtendsTypes();
+
+ /** @return true if the all extend type is specified, implying all other types */
+ public boolean isAllInherited();
+
+ /** @return true if parent info attributes are inherited (organisation, branch, revision, etc)*/
+ public boolean isInfoInherited();
+
+ /** @return true if parent description is inherited */
+ public boolean isDescriptionInherited();
+
+ /** @return true if parent configurations are inherited */
+ public boolean areConfigurationsInherited();
+
+ /** @return true if parent dependencies are inherited */
+ public boolean areDependenciesInherited();
+}
Index: src/java/org/apache/ivy/core/module/descriptor/InheritableItem.java
===================================================================
--- src/java/org/apache/ivy/core/module/descriptor/InheritableItem.java (revision 0)
+++ src/java/org/apache/ivy/core/module/descriptor/InheritableItem.java (revision 0)
@@ -0,0 +1,34 @@
+/*
+ * 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 org.apache.ivy.core.module.id.ModuleRevisionId;
+
+/**
+ * Interface for elements that can be inherited from a parent descriptor
+ * by a child descriptor.
+ * @see Configuration
+ * @see DependencyDescriptor
+ */
+public interface InheritableItem {
+ /**
+ * @return the module in which this item was actually defined, if different
+ * from the module in which the item appears. May be null.
+ */
+ public ModuleRevisionId getSourceModule();
+}
Index: src/java/org/apache/ivy/core/module/descriptor/ModuleDescriptor.java
===================================================================
--- src/java/org/apache/ivy/core/module/descriptor/ModuleDescriptor.java (revision 920176)
+++ src/java/org/apache/ivy/core/module/descriptor/ModuleDescriptor.java (working copy)
@@ -72,6 +72,13 @@
void setResolvedModuleRevisionId(ModuleRevisionId revId);
/**
+ * Get the list of parent descriptors imported via an <extends> element.
+ * Only directly imported descriptors are included; the parent's parents are
+ * not included.
+ */
+ ExtendsDescriptor[] getInheritedDescriptors();
+
+ /**
* This method update the resolved publication date
*
* @param publicationDate
Index: src/java/org/apache/ivy/core/publish/PublishEngine.java
===================================================================
--- src/java/org/apache/ivy/core/publish/PublishEngine.java (revision 920176)
+++ src/java/org/apache/ivy/core/publish/PublishEngine.java (working copy)
@@ -133,6 +133,8 @@
.setBranch(options.getPubBranch())
.setPubdate(options.getPubdate() == null ? new Date()
: options.getPubdate())
+ .setMerge(options.isMerge())
+ .setMergedDescriptor(md)
.setConfsToExclude((String[]) confsToRemove
.toArray(new String[confsToRemove.size()])));
ivyFile = tmp;
Index: src/java/org/apache/ivy/core/publish/PublishOptions.java
===================================================================
--- src/java/org/apache/ivy/core/publish/PublishOptions.java (revision 920176)
+++ src/java/org/apache/ivy/core/publish/PublishOptions.java (working copy)
@@ -52,6 +52,8 @@
private boolean overwrite;
private boolean update;
+
+ private Boolean merge;
private String[] confs;
@@ -132,6 +134,15 @@
this.update = update;
return this;
}
+
+ public boolean isMerge() {
+ return merge == null ? isUpdate() : merge.booleanValue();
+ }
+
+ public PublishOptions setMerge(Boolean merge) {
+ this.merge = merge;
+ return this;
+ }
public boolean isValidate() {
return validate;
Index: src/java/org/apache/ivy/plugins/parser/xml/UpdateOptions.java
===================================================================
--- src/java/org/apache/ivy/plugins/parser/xml/UpdateOptions.java (revision 920176)
+++ src/java/org/apache/ivy/plugins/parser/xml/UpdateOptions.java (working copy)
@@ -21,6 +21,7 @@
import java.util.Date;
import java.util.Map;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
import org.apache.ivy.plugins.namespace.Namespace;
import org.apache.ivy.plugins.parser.ParserSettings;
@@ -54,6 +55,11 @@
*/
private boolean replaceInclude = true;
/**
+ * Should parent descriptor be merged inline
+ */
+ private boolean merge = false;
+ private ModuleDescriptor mergedDescriptor = null;
+ /**
* Configurations to exclude during update, or null to keep all confs.
*/
private String[] confsToExclude = null;
@@ -118,6 +124,20 @@
this.replaceInclude = replaceInclude;
return this;
}
+ public boolean isMerge() {
+ return merge;
+ }
+ public UpdateOptions setMerge(boolean merge) {
+ this.merge = merge;
+ return this;
+ }
+ public ModuleDescriptor getMergedDescriptor() {
+ return mergedDescriptor;
+ }
+ public UpdateOptions setMergedDescriptor(ModuleDescriptor mergedDescriptor) {
+ this.mergedDescriptor = mergedDescriptor;
+ return this;
+ }
public String[] getConfsToExclude() {
return confsToExclude;
}
Index: src/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorParser.java
===================================================================
--- src/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorParser.java (revision 920176)
+++ src/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorParser.java (working copy)
@@ -24,6 +24,7 @@
import java.net.URL;
import java.text.ParseException;
import java.util.Arrays;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -31,33 +32,26 @@
import org.apache.ivy.Ivy;
import org.apache.ivy.core.IvyContext;
-import org.apache.ivy.core.module.descriptor.Configuration;
-import org.apache.ivy.core.module.descriptor.ConfigurationAware;
-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.DefaultIncludeRule;
-import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
-import org.apache.ivy.core.module.descriptor.DependencyArtifactDescriptor;
-import org.apache.ivy.core.module.descriptor.ExcludeRule;
-import org.apache.ivy.core.module.descriptor.IncludeRule;
-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.cache.ResolutionCacheManager;
+import org.apache.ivy.core.module.descriptor.*;
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.core.resolve.ResolveData;
+import org.apache.ivy.core.resolve.ResolveEngine;
+import org.apache.ivy.core.resolve.ResolveOptions;
+import org.apache.ivy.core.resolve.ResolvedModuleRevision;
import org.apache.ivy.plugins.conflict.ConflictManager;
import org.apache.ivy.plugins.conflict.FixedConflictManager;
import org.apache.ivy.plugins.matcher.PatternMatcher;
import org.apache.ivy.plugins.namespace.Namespace;
import org.apache.ivy.plugins.parser.AbstractModuleDescriptorParser;
import org.apache.ivy.plugins.parser.ModuleDescriptorParser;
+import org.apache.ivy.plugins.parser.ModuleDescriptorParserRegistry;
import org.apache.ivy.plugins.parser.ParserSettings;
import org.apache.ivy.plugins.repository.Resource;
import org.apache.ivy.plugins.repository.url.URLResource;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
import org.apache.ivy.util.Message;
import org.apache.ivy.util.XMLHelper;
import org.apache.ivy.util.extendable.ExtendableItemHelper;
@@ -269,13 +263,15 @@
throws SAXException {
try {
if (state == State.DESCRIPTION) {
- //make sure we don't interpret any tag while in description tag
+ // make sure we don't interpret any tag while in description tag
buffer.append("<" + qName + ">");
return;
} else if ("ivy-module".equals(qName)) {
ivyModuleStarted(attributes);
} else if ("info".equals(qName)) {
infoStarted(attributes);
+ } else if (state == State.INFO && "extends".equals(qName)) {
+ extendsStarted(attributes);
} else if (state == State.INFO && "license".equals(qName)) {
getMd().addLicense(new License(settings.substitute(attributes.getValue("name")),
settings.substitute(attributes.getValue("url"))));
@@ -341,6 +337,250 @@
}
}
+ protected String getDefaultParentLocation() {
+ return "../ivy.xml";
+ }
+
+ protected void extendsStarted(Attributes attributes) throws ParseException {
+ String parentOrganisation = attributes.getValue("organisation");
+ String parentModule = attributes.getValue("module");
+ String parentRevision = attributes.getValue("revision");
+ String location = attributes.getValue("location") != null ? attributes
+ .getValue("location") : getDefaultParentLocation();
+ ModuleDescriptor parent = null;
+
+ String extendType = attributes.getValue("extendType") != null ? attributes.getValue(
+ "extendType").toLowerCase() : "all";
+
+ List/* */extendTypes = Arrays.asList(extendType.split(","));
+
+ try {
+ Message.debug("Trying to parse included ivy file :" + location);
+ parent = parseOtherIvyFileOnFileSystem(location);
+
+ //verify that the parsed descriptor is the correct parent module.
+ ModuleId expected = new ModuleId(parentOrganisation, parentModule);
+ ModuleId pid = parent.getModuleRevisionId().getModuleId();
+ if (!expected.equals(pid)) {
+ Message.verbose("Ignoring parent Ivy file " + location + "; expected "
+ + expected + " but found " + pid);
+ parent = null;
+ }
+
+ } catch (ParseException e) {
+ Message.warn("Unable to parse included ivy file " + location + ": "
+ + e.getMessage());
+ } catch (IOException e) {
+ Message.warn("Unable to parse included ivy file " + location + ": "
+ + e.getMessage());
+ }
+
+ // if the included ivy file is not found on file system, tries to resolve using
+ // repositories
+ if (parent == null) {
+ try {
+ Message.debug(
+ "Trying to parse included ivy file by asking repository for module :"
+ + parentOrganisation
+ + "#"
+ + parentModule
+ + ";"
+ + parentRevision);
+ parent = parseOtherIvyFile(parentOrganisation, parentModule, parentRevision);
+ } catch (ParseException e) {
+ Message.warn("Unable to parse included ivy file for " + parentOrganisation
+ + "#" + parentModule + ";" + parentRevision);
+ }
+ }
+
+ if (parent == null) {
+ throw new ParseException("Unable to parse included ivy file for "
+ + parentOrganisation + "#" + parentModule + ";" + parentRevision, 0);
+ }
+
+ ResolutionCacheManager cacheManager = settings.getResolutionCacheManager();
+
+ File ivyFileInCache = cacheManager.getResolvedIvyFileInCache(parent
+ .getResolvedModuleRevisionId());
+ //Generate the parent cache file if necessary
+ if (parent.getResource() != null
+ && !parent.getResource().getName().equals(ivyFileInCache.toURI().toString())) {
+ try {
+ parent.toIvyFile(ivyFileInCache);
+ } catch (ParseException e) {
+ throw new ParseException("Unable to create cache file for "
+ + parentOrganisation + "#" + parentModule + ";" + parentRevision
+ + " Reason:" + e.getLocalizedMessage(), 0);
+ } catch (IOException e) {
+ throw new ParseException("Unable to create cache file for "
+ + parentOrganisation + "#" + parentModule + ";" + parentRevision
+ + " Reason :" + e.getLocalizedMessage(), 0);
+ }
+ }
+
+ DefaultExtendsDescriptor ed = new DefaultExtendsDescriptor(
+ parent.getModuleRevisionId(),
+ parent.getResolvedModuleRevisionId(),
+ attributes.getValue("location"),
+ (String[])extendTypes.toArray(new String[extendTypes.size()]));
+ getMd().addInheritedDescriptor(ed);
+
+ mergeWithOtherModuleDescriptor(extendTypes, parent);
+ }
+
+ protected void mergeWithOtherModuleDescriptor(List/* */extendTypes,
+ ModuleDescriptor parent) {
+
+ if (extendTypes.contains("all")) {
+ mergeAll(parent);
+ } else {
+ if (extendTypes.contains("info")) {
+ mergeInfo(parent);
+ }
+
+ if (extendTypes.contains("configurations")) {
+ mergeConfigurations(parent.getModuleRevisionId(), parent.getConfigurations());
+ }
+
+ if (extendTypes.contains("dependencies")) {
+ mergeDependencies(parent.getDependencies());
+ }
+
+ if (extendTypes.contains("description")) {
+ mergeDescription(parent.getDescription());
+ }
+ }
+
+ }
+
+ protected void mergeAll(ModuleDescriptor parent) {
+ ModuleRevisionId sourceMrid = parent.getModuleRevisionId();
+ mergeInfo(parent);
+ mergeConfigurations(sourceMrid, parent.getConfigurations());
+ mergeDependencies(parent.getDependencies());
+ mergeDescription(parent.getDescription());
+ }
+
+ protected void mergeInfo(ModuleDescriptor parent) {
+ ModuleRevisionId parentMrid = parent.getModuleRevisionId();
+
+ DefaultModuleDescriptor descriptor = getMd();
+ ModuleRevisionId currentMrid = descriptor.getModuleRevisionId();
+
+ ModuleRevisionId mergedMrid = ModuleRevisionId.newInstance(
+ mergeValue(parentMrid.getOrganisation(), currentMrid.getOrganisation()),
+ currentMrid.getName(),
+ mergeValue(parentMrid.getBranch(), currentMrid.getBranch()),
+ mergeValue(parentMrid.getRevision(), currentMrid.getRevision()),
+ mergeValues(parentMrid.getQualifiedExtraAttributes(),
+ currentMrid.getQualifiedExtraAttributes())
+ );
+
+ descriptor.setModuleRevisionId(mergedMrid);
+ descriptor.setResolvedModuleRevisionId(mergedMrid);
+
+ descriptor.setStatus(mergeValue(parent.getStatus(), descriptor.getStatus()));
+ if (descriptor.getNamespace() == null && parent instanceof DefaultModuleDescriptor) {
+ descriptor.setNamespace(((DefaultModuleDescriptor)parent).getNamespace());
+ }
+ }
+
+ private static String mergeValue(String inherited, String override) {
+ return override == null ? inherited : override;
+ }
+
+ private static Map mergeValues(Map inherited, Map overrides) {
+ LinkedHashMap dup = new LinkedHashMap(inherited.size() + overrides.size());
+ dup.putAll(inherited);
+ dup.putAll(overrides);
+ return dup;
+ }
+
+ protected void mergeConfigurations(ModuleRevisionId sourceMrid, Configuration[] configurations) {
+ DefaultModuleDescriptor md = getMd();
+ for (int i = 0; i < configurations.length; i++) {
+ Configuration configuration = configurations[i];
+ Message.debug("Merging configuration with: " + configuration.getName());
+ //copy configuration from parent descriptor
+ md.addConfiguration(new Configuration(configuration, sourceMrid));
+ }
+ }
+
+ protected void mergeDependencies(DependencyDescriptor[] dependencies) {
+ DefaultModuleDescriptor md = getMd();
+ for (int i = 0; i < dependencies.length; i++) {
+ DependencyDescriptor dependencyDescriptor = dependencies[i];
+ Message.debug("Merging dependency with: "
+ + dependencyDescriptor.getDependencyRevisionId().toString());
+ md.addDependency(dependencyDescriptor);
+ }
+ }
+
+ protected void mergeDescription(String description) {
+ String current = getMd().getDescription();
+ if (current == null || current.trim().length() == 0) {
+ getMd().setDescription(description);
+ }
+ }
+
+ protected ModuleDescriptor parseOtherIvyFileOnFileSystem(String location)
+ throws ParseException, IOException {
+ URL url = null;
+ ModuleDescriptor parent = null;
+ url = getSettings().getRelativeUrlResolver().getURL(descriptorURL, location);
+ Message.debug("Trying to load included ivy file from " + url.toString());
+ URLResource res = new URLResource(url);
+ ModuleDescriptorParser parser = ModuleDescriptorParserRegistry.getInstance().getParser(
+ res);
+
+ parent = parser.parseDescriptor(getSettings(), url, isValidate());
+ return parent;
+ }
+
+ protected ModuleDescriptor parseOtherIvyFile(String parentOrganisation,
+ String parentModule, String parentRevision) throws ParseException {
+ ModuleId parentModuleId = new ModuleId(parentOrganisation, parentModule);
+ ModuleRevisionId parentMrid = new ModuleRevisionId(parentModuleId, parentRevision);
+
+ // try to load parent module in cache
+ File cacheFile = settings.getResolutionCacheManager().getResolvedIvyFileInCache(
+ ModuleRevisionId.newInstance(parentMrid, Ivy.getWorkingRevision()));
+ if (cacheFile.exists() && cacheFile.length() > 0) {
+ ModuleDescriptor md;
+ try {
+ Message.debug("Trying to load included ivy file from cache");
+ URL parentUrl = cacheFile.toURI().toURL();
+ md = parseOtherIvyFileOnFileSystem(parentUrl.toString());
+ return md;
+ } catch (IOException e) {
+ // do nothing
+ Message.error(e.getLocalizedMessage());
+ }
+ }
+
+ DependencyDescriptor dd = new DefaultDependencyDescriptor(parentMrid, true);
+ ResolveData data = IvyContext.getContext().getResolveData();
+ if (data == null) {
+ ResolveEngine engine = IvyContext.getContext().getIvy().getResolveEngine();
+ ResolveOptions options = new ResolveOptions();
+ options.setDownload(false);
+ data = new ResolveData(engine, options);
+ }
+
+ DependencyResolver resolver = getSettings().getResolver(parentMrid);
+ if (resolver == null) {
+ // TODO: Throw exception here?
+ return null;
+ } else {
+ ResolvedModuleRevision otherModule = resolver.getDependency(dd, data);
+ if (otherModule == null) {
+ throw new ParseException("Unable to find " + parentMrid.toString(), 0);
+ }
+ return otherModule.getDescriptor();
+ }
+
+ }
+
protected void publicationsStarted(Attributes attributes) {
state = State.PUB;
artifactsDeclared = true;
Index: src/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorUpdater.java
===================================================================
--- src/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorUpdater.java (revision 920176)
+++ src/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorUpdater.java (working copy)
@@ -27,21 +27,14 @@
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
+import java.io.StringWriter;
import java.net.URL;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Stack;
-import java.util.StringTokenizer;
+import java.util.*;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.ivy.Ivy;
+import org.apache.ivy.core.module.descriptor.*;
import org.apache.ivy.core.module.id.ModuleId;
import org.apache.ivy.core.module.id.ModuleRevisionId;
import org.apache.ivy.plugins.namespace.NameSpaceHelper;
@@ -153,6 +146,23 @@
private static class UpdaterHandler extends DefaultHandler implements LexicalHandler {
+ /** standard attributes of ivy-module/info */
+ private static final Collection stdAtts = Arrays.asList(new String[] {"organisation", "module", "branch",
+ "revision", "status", "publication", "namespace"});
+
+ /** elements that may appear inside ivy-module, in expected order */
+ private static final List moduleElements = Arrays.asList(new String[] {
+ "info", "configurations", "publications", "dependencies", "conflicts"
+ });
+ /** element position of "configurations" inside "ivy-module" */
+ private static final int CONFIGURATIONS_POSITION = moduleElements.indexOf("configurations");
+ /** element position of "dependencies" inside "ivy-module" */
+ private static final int DEPENDENCIES_POSITION = moduleElements.indexOf("dependencies");
+
+ /** elements that may appear inside of ivy-module/info */
+ private static final Collection infoElements = Arrays.asList(new String[]{
+ "extends", "ivyauthor", "license", "repository", "description" });
+
private final ParserSettings settings;
private final PrintWriter out;
@@ -213,6 +223,21 @@
// with /> instead of >
private String justOpen = null;
+ //track the size of the left indent, so that inserted elements are formatted
+ //like nearby elements.
+
+ //true when we're reading indent whitespace
+ private boolean indenting;
+ private StringBuffer currentIndent = new StringBuffer();
+ private ArrayList indentLevels = new ArrayList(); // ArrayList
+
+ //true if an ivy-module/info/description element has been found in the published descriptor
+ private boolean hasDescription = false;
+ //true if merged configurations have been written
+ private boolean mergedConfigurations = false;
+ //true if merged deps have been written
+ private boolean mergedDependencies = false;
+
// the new value of the defaultconf attribute on the publications tag
private String newDefaultConf = null;
@@ -225,10 +250,15 @@
public void startElement(String uri, String localName, String qName, Attributes attributes)
throws SAXException {
inHeader = false;
+ endIndent();
if (justOpen != null) {
write(">");
}
+
+ flushMergedElementsBefore(qName);
context.push(qName);
+
+ String path = getContext();
if ("info".equals(qName)) {
infoStarted(attributes);
} else if (replaceInclude && "include".equals(qName)
@@ -236,15 +266,17 @@
//TODO, in the case of !replaceInclude, we should still replace the relative path
//by an absolute path.
includeStarted(attributes);
- } else if ("ivy-module/dependencies/dependency".equals(getContext())) {
+ } else if ("ivy-module/info/extends".equals(path)) {
+ startExtends(attributes);
+ } else if ("ivy-module/dependencies/dependency".equals(path)) {
startElementInDependency(attributes);
} else if ("dependencies".equals(qName)) {
startDependencies(attributes);
- } else if ("ivy-module/configurations/conf".equals(getContext())) {
+ } else if ("ivy-module/configurations/conf".equals(path)) {
startElementInConfigurationsConf(qName, attributes);
- } else if ("ivy-module/publications/artifact/conf".equals(getContext())
- || "ivy-module/dependencies/dependency/conf".equals(getContext())
- || "ivy-module/dependencies/dependency/artifact/conf".equals(getContext())) {
+ } else if ("ivy-module/publications/artifact/conf".equals(path)
+ || "ivy-module/dependencies/dependency/conf".equals(path)
+ || "ivy-module/dependencies/dependency/artifact/conf".equals(path)) {
buffers.push(new ExtendedBuffer(getContext()));
((ExtendedBuffer) confAttributeBuffers.peek()).setDefaultPrint(false);
String confName = substitute(settings, attributes.getValue("name"));
@@ -257,7 +289,7 @@
+ substitute(settings, attributes.getValue(i)) + "\"");
}
}
- } else if ("ivy-module/publications/artifact".equals(getContext())) {
+ } else if ("ivy-module/publications/artifact".equals(path)) {
ExtendedBuffer buffer = new ExtendedBuffer(getContext());
buffers.push(buffer);
confAttributeBuffers.push(buffer);
@@ -278,7 +310,7 @@
+ substitute(settings, attributes.getValue(i)) + "\"");
}
}
- } else if ("ivy-module/dependencies/dependency/artifact".equals(getContext())) {
+ } else if ("ivy-module/dependencies/dependency/artifact".equals(path)) {
ExtendedBuffer buffer = new ExtendedBuffer(getContext());
buffers.push(buffer);
confAttributeBuffers.push(buffer);
@@ -298,9 +330,22 @@
+ substitute(settings, attributes.getValue(i)) + "\"");
}
}
- } else if ("ivy-module/publications".equals(getContext())) {
+ } else if ("ivy-module/publications".equals(path)) {
startPublications(attributes);
} else {
+ if (options.isMerge() && path.startsWith("ivy-module/info")) {
+ ModuleDescriptor merged = options.getMergedDescriptor();
+ if (path.equals("ivy-module/info/description")) {
+ //if the descriptor already contains a description, don't bother printing
+ //the merged version.
+ hasDescription = true;
+ } else if (!infoElements.contains(qName)) {
+ //according to the XSD, we should write description after all of the other
+ //standard elements but before any extended elements.
+ writeInheritedDescription(merged);
+ }
+ }
+
// copy
write("<" + qName);
for (int i = 0; i < attributes.getLength(); i++) {
@@ -312,6 +357,47 @@
// indent.append("\t");
}
+ private void startExtends(Attributes attributes) {
+ // in merge mode, comment out extends element
+ if (options.isMerge()) {
+ write("");
+ }
+
+ /**
+ * Collect the given list of inherited descriptor items into lists keyed by parent Id.
+ * Thus all of the items inherited from parent A can be written together, then all of
+ * the items from parent B, and so on.
+ * @param merged the merged child descriptor
+ * @param items the inherited items to collate
+ * @return maps parent ModuleRevisionId to a List of InheritedItems imported from that parent
+ */
+ private Map/**/ collateInheritedItems(ModuleDescriptor merged,
+ InheritableItem[] items) {
+ LinkedHashMap/**/ inheritedItems = new LinkedHashMap();
+ for (int i = 0; i < items.length; ++i) {
+ ModuleRevisionId source = items[i].getSourceModule();
+ //ignore items that are defined directly in the child descriptor
+ if (source != null
+ && !source.getModuleId().equals(merged.getModuleRevisionId().getModuleId())) {
+ List accum = (List) inheritedItems.get(source);
+ if (accum == null) {
+ accum = new ArrayList();
+ inheritedItems.put(source, accum);
+ }
+ accum.add(items[i]);
+ }
+ }
+ return inheritedItems;
+ }
+
+ /**
+ * If no info/description element has yet been written, write the description inherited from
+ * the parent descriptor, if any. Calling this method more than once has no affect.
+ */
+ private void writeInheritedDescription(ModuleDescriptor merged) {
+ if (!hasDescription) {
+ hasDescription = true;
+ String description = merged.getDescription();
+ if (description != null) {
+ PrintWriter writer = getWriter();
+ if (justOpen != null) {
+ writer.println(">");
+ }
+ writeInheritanceComment("description", "parent");
+ writer.println(getIndent() + "" + XMLHelper.escape(description) + "");
+ //restore the indent that existed before we wrote the extra elements
+ writer.print(currentIndent);
+ justOpen = null;
+ }
+ }
+ }
+
+ private void writeInheritedConfigurations(ModuleDescriptor merged) {
+ if (!mergedConfigurations) {
+ mergedConfigurations = true;
+ writeInheritedItems(merged, merged.getConfigurations(),
+ ConfigurationPrinter.INSTANCE, "configurations", false);
+ }
+ }
+
+ private void writeInheritedDependencies(ModuleDescriptor merged) {
+ if (!mergedDependencies) {
+ mergedDependencies = true;
+ writeInheritedItems(merged, merged.getDependencies(),
+ DependencyPrinter.INSTANCE, "dependencies", false);
+ }
}
+ /**
+ * If publishing in merge mode, guarantee that any merged elements appearing
+ * before moduleElement have been written. This method should
+ * be called before we write the start tag of moduleElement.
+ * This covers cases where merged elements like "configurations" and "dependencies" appear
+ * in the parent descriptor, but are completely missing in the child descriptor.
+ *
+ * For example, if "moduleElement" is "dependencies", guarantees that "configurations"
+ * has been written. If moduleElement is null, then all
+ * missing merged elements will be flushed.
+ *
+ * @param moduleElement a descriptor element name, for example "configurations" or "info"
+ */
+ private void flushMergedElementsBefore(String moduleElement) {
+ if (options.isMerge() && context.size() == 1 && "ivy-module".equals(context.peek())
+ && !(mergedConfigurations && mergedDependencies)) {
+
+ //calculate the position of the element in ivy-module
+ int position = moduleElement == null ? moduleElements.size()
+ : moduleElements.indexOf(moduleElement);
+
+ ModuleDescriptor merged = options.getMergedDescriptor();
+ PrintWriter out = getWriter();
+
+ //see if we should write
+ if (!mergedConfigurations && position > CONFIGURATIONS_POSITION
+ && merged.getConfigurations().length > 0) {
+
+ mergedConfigurations = true;
+ writeInheritedItems(merged, merged.getConfigurations(),
+ ConfigurationPrinter.INSTANCE, "configurations", true);
+
+ }
+ //see if we should write
+ if (!mergedDependencies && position > DEPENDENCIES_POSITION
+ && merged.getDependencies().length > 0) {
+
+ mergedDependencies = true;
+ writeInheritedItems(merged, merged.getDependencies(),
+ DependencyPrinter.INSTANCE, "dependencies", true);
+
+ }
+ }
+ }
+ private void flushAllMergedElements() {
+ flushMergedElementsBefore(null);
+ }
+
public void endElement(String uri, String localName, String qName) throws SAXException {
+
+ String path = getContext();
+ if (options.isMerge()) {
+ ModuleDescriptor merged = options.getMergedDescriptor();
+
+ if ("ivy-module/info".equals(path)) {
+ //guarantee that inherited description has been written before
+ //info element closes.
+ writeInheritedDescription(merged);
+ } else if ("ivy-module/configurations".equals(path)) {
+ //write inherited configurations after all child configurations
+ writeInheritedConfigurations(merged);
+ } else if ("ivy-module/dependencies".equals(path)) {
+ //write inherited dependencies after all child dependencies
+ writeInheritedDependencies(merged);
+ } else if ("ivy-module".equals(path)) {
+ //write any remaining inherited data before we close the
+ //descriptor.
+ flushAllMergedElements();
+ }
+ }
+
if (qName.equals(justOpen)) {
write("/>");
} else {
@@ -701,21 +1093,26 @@
if (!buffers.isEmpty()) {
ExtendedBuffer buffer = (ExtendedBuffer) buffers.peek();
- if (buffer.getContext().equals(getContext())) {
+ if (buffer.getContext().equals(path)) {
buffers.pop();
if (buffer.isPrint()) {
- write(buffer.getBuffer().toString());
+ write(buffer.toString());
}
}
}
if (!confAttributeBuffers.isEmpty()) {
ExtendedBuffer buffer = (ExtendedBuffer) confAttributeBuffers.peek();
- if (buffer.getContext().equals(getContext())) {
+ if (buffer.getContext().equals(path)) {
confAttributeBuffers.pop();
}
}
+ // element is commented out when in merge mode.
+ if (options.isMerge() && "ivy-module/info/extends".equals(path)) {
+ write(" -->");
+ }
+
justOpen = null;
context.pop();
}
@@ -834,7 +1231,9 @@
private boolean defaultPrint = false;
- private StringBuffer buffer = new StringBuffer();
+ private StringWriter buffer = new StringWriter();
+
+ private PrintWriter writer = new PrintWriter(buffer);
ExtendedBuffer(String context) {
this.context = context;
@@ -855,12 +1254,48 @@
this.defaultPrint = print;
}
- StringBuffer getBuffer() {
- return buffer;
+ PrintWriter getWriter() {
+ return writer;
}
String getContext() {
return context;
}
+
+ public String toString() {
+ writer.flush();
+ return buffer.toString();
+ }
+ }
+
+ /**
+ * Prints a descriptor item's XML representation
+ */
+ protected static interface ItemPrinter {
+ /**
+ * Print an XML representation of item to out.
+ * @param parent the module descriptor containing item
+ * @param item subcomponent of the descriptor, for example a {@link DependencyDescriptor}
+ * or {@link Configuration}
+ */
+ public void print(ModuleDescriptor parent, Object item, PrintWriter out);
+ }
+
+ protected static class DependencyPrinter implements ItemPrinter {
+
+ public static final DependencyPrinter INSTANCE = new DependencyPrinter();
+
+ public void print(ModuleDescriptor parent, Object item, PrintWriter out) {
+ XmlModuleDescriptorWriter.printDependency(parent, (DependencyDescriptor) item, out);
+ }
+ }
+
+ protected static class ConfigurationPrinter implements ItemPrinter {
+
+ public static final ConfigurationPrinter INSTANCE = new ConfigurationPrinter();
+
+ public void print(ModuleDescriptor parent, Object item, PrintWriter out) {
+ XmlModuleDescriptorWriter.printConfiguration((Configuration) item, out);
+ }
}
}
Index: src/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorWriter.java
===================================================================
--- src/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorWriter.java (revision 920176)
+++ src/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorWriter.java (working copy)
@@ -29,19 +29,11 @@
import org.apache.ivy.Ivy;
import org.apache.ivy.core.IvyPatternHelper;
-import org.apache.ivy.core.module.descriptor.Artifact;
-import org.apache.ivy.core.module.descriptor.Configuration;
-import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
-import org.apache.ivy.core.module.descriptor.DependencyArtifactDescriptor;
-import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
-import org.apache.ivy.core.module.descriptor.DependencyDescriptorMediator;
-import org.apache.ivy.core.module.descriptor.ExcludeRule;
-import org.apache.ivy.core.module.descriptor.IncludeRule;
-import org.apache.ivy.core.module.descriptor.License;
-import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
-import org.apache.ivy.core.module.descriptor.OverrideDependencyDescriptorMediator;
+import org.apache.ivy.core.module.descriptor.*;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
import org.apache.ivy.plugins.matcher.MapMatcher;
import org.apache.ivy.util.Message;
+import org.apache.ivy.util.StringUtils;
import org.apache.ivy.util.XMLHelper;
import org.apache.ivy.util.extendable.ExtendableItem;
@@ -93,82 +85,89 @@
if (dds.length > 0) {
out.println("\t");
for (int i = 0; i < dds.length; i++) {
- out.print("\t\t");
- for (int k = 0; k < depConfs.length; k++) {
- out.print(XMLHelper.escape(depConfs[k]));
- if (k + 1 < depConfs.length) {
- out.print(",");
- }
- }
- if (j + 1 < modConfs.length) {
- out.print(";");
- }
- }
- out.print("\"");
-
- printExtraAttributes(dds[i], out, " ");
-
- DependencyArtifactDescriptor[] depArtifacts = dds[i].getAllDependencyArtifacts();
- if (depArtifacts.length > 0) {
- out.println(">");
- }
- printDependencyArtefacts(md, out, depArtifacts);
-
- IncludeRule[] includes = dds[i].getAllIncludeRules();
- if (includes.length > 0 && depArtifacts.length == 0) {
- out.println(">");
- }
- printDependencyIncludeRules(md, out, includes);
-
- ExcludeRule[] excludes = dds[i].getAllExcludeRules();
- if (excludes.length > 0 && includes.length == 0 && depArtifacts.length == 0) {
- out.println(">");
- }
- printDependencyExcludeRules(md, out, excludes);
- if (includes.length + excludes.length + depArtifacts.length == 0) {
- out.println("/>");
- } else {
- out.println("\t\t");
- }
+ DependencyDescriptor dep = dds[i];
+ out.print("\t\t");
+ printDependency(md, dep, out);
}
printAllExcludes(md, out);
printAllMediators(md, out);
out.println("\t");
}
}
+
+ protected static void printDependency(ModuleDescriptor md, DependencyDescriptor dep,
+ PrintWriter out) {
+ out.print("");
+ for (int k = 0; k < depConfs.length; k++) {
+ out.print(XMLHelper.escape(depConfs[k]));
+ if (k + 1 < depConfs.length) {
+ out.print(",");
+ }
+ }
+ if (j + 1 < modConfs.length) {
+ out.print(";");
+ }
+ }
+ out.print("\"");
+
+ printExtraAttributes(dep, out, " ");
+
+ DependencyArtifactDescriptor[] depArtifacts = dep.getAllDependencyArtifacts();
+ if (depArtifacts.length > 0) {
+ out.println(">");
+ }
+ printDependencyArtefacts(md, out, depArtifacts);
+
+ IncludeRule[] includes = dep.getAllIncludeRules();
+ if (includes.length > 0 && depArtifacts.length == 0) {
+ out.println(">");
+ }
+ printDependencyIncludeRules(md, out, includes);
+
+ ExcludeRule[] excludes = dep.getAllExcludeRules();
+ if (excludes.length > 0 && includes.length == 0 && depArtifacts.length == 0) {
+ out.println(">");
+ }
+ printDependencyExcludeRules(md, out, excludes);
+ if (includes.length + excludes.length + depArtifacts.length == 0) {
+ out.println("/>");
+ } else {
+ out.println("\t\t");
+ }
+ }
private static void printAllMediators(ModuleDescriptor md, PrintWriter out) {
Map/**/ mediators
@@ -381,34 +380,40 @@
if (confs.length > 0) {
out.println("\t");
for (int i = 0; i < confs.length; i++) {
- out.print("\t\t 0) {
- out.print(" extends=\"");
- for (int j = 0; j < exts.length; j++) {
- out.print(XMLHelper.escape(exts[j]));
- if (j + 1 < exts.length) {
- out.print(",");
- }
- }
- out.print("\"");
- }
- if (confs[i].getDeprecated() != null) {
- out.print(" deprecated=\"" + XMLHelper.escape(confs[i].getDeprecated()) + "\"");
- }
- printExtraAttributes(confs[i], out, " ");
- out.println("/>");
+ Configuration conf = confs[i];
+ out.print("\t\t");
+ printConfiguration(conf, out);
}
out.println("\t");
}
}
+
+ protected static void printConfiguration(Configuration conf, PrintWriter out) {
+ out.print(" 0) {
+ out.print(" extends=\"");
+ for (int j = 0; j < exts.length; j++) {
+ out.print(XMLHelper.escape(exts[j]));
+ if (j + 1 < exts.length) {
+ out.print(",");
+ }
+ }
+ out.print("\"");
+ }
+ if (conf.getDeprecated() != null) {
+ out.print(" deprecated=\"" + XMLHelper.escape(conf.getDeprecated()) + "\"");
+ }
+ printExtraAttributes(conf, out, " ");
+ out.println("/>");
+ }
private static void printInfoTag(ModuleDescriptor md, PrintWriter out) {
out.println("\t");
+ ExtendsDescriptor[] parents = md.getInheritedDescriptors();
+ for (int i = 0; i < parents.length; i++) {
+ ExtendsDescriptor parent = parents[i];
+ ModuleRevisionId mrid = parent.getParentRevisionId();
+ out.print("\t\t");
+ }
License[] licenses = md.getLicenses();
for (int i = 0; i < licenses.length; i++) {
License license = licenses[i];
@@ -492,7 +512,8 @@
return md.getExtraInfo().size() > 0
|| md.getHomePage() != null
|| (md.getDescription() != null && md.getDescription().trim().length() > 0)
- || md.getLicenses().length > 0;
+ || md.getLicenses().length > 0
+ || md.getInheritedDescriptors().length > 0;
}
private static String getConfs(ModuleDescriptor md, Artifact artifact) {
Index: src/java/org/apache/ivy/plugins/parser/xml/ivy.xsd
===================================================================
--- src/java/org/apache/ivy/plugins/parser/xml/ivy.xsd (revision 920176)
+++ src/java/org/apache/ivy/plugins/parser/xml/ivy.xsd (working copy)
@@ -60,6 +60,15 @@
+
+
+
+
+
+
+
+
+
@@ -91,7 +100,7 @@
-
+
Index: test/java/org/apache/ivy/ant/IvyDeliverTest.java
===================================================================
--- test/java/org/apache/ivy/ant/IvyDeliverTest.java (revision 920176)
+++ test/java/org/apache/ivy/ant/IvyDeliverTest.java (working copy)
@@ -20,6 +20,9 @@
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.text.ParseException;
import java.util.HashMap;
import java.util.Map;
@@ -87,6 +90,61 @@
del.execute();
}
+ public void testMergeParent() throws IOException, ParseException {
+ //publish the parent descriptor first, so that it can be found while
+ //we are reading the child descriptor.
+ project.setProperty("ivy.dep.file", "test/java/org/apache/ivy/ant/ivy-multiconf.xml");
+ IvyResolve res = new IvyResolve();
+ res.setProject(project);
+ res.execute();
+
+ IvyPublish pubParent = new IvyPublish();
+ pubParent.setProject(project);
+ pubParent.setResolver("1");
+ pubParent.setPubrevision("1.0");
+ File art = new File("build/test/deliver/resolve-simple-1.0.jar");
+ FileUtil.copy(new File("test/repositories/1/org1/mod1.1/jars/mod1.1-1.0.jar"), art, null);
+ pubParent.execute();
+
+ //resolve and deliver the child descriptor
+ project.setProperty("ivy.dep.file", "test/java/org/apache/ivy/ant/ivy-extends-multiconf.xml");
+ res = new IvyResolve();
+ res.setProject(project);
+ res.execute();
+
+ deliver.setMerge(true);
+ deliver.setPubrevision("1.2");
+ deliver.setDeliverpattern("build/test/deliver/merge/ivy-[revision].xml");
+ deliver.execute();
+
+ // should have delivered the file to the specified destination
+ File delivered = new File("build/test/deliver/merge/ivy-1.2.xml");
+ assertTrue(delivered.exists());
+
+ // do a text compare, since we want to test comments as well as structure.
+ // we could do a better job of this with xmlunit
+
+ int lineNo = 1;
+
+ BufferedReader merged = new BufferedReader(new FileReader(delivered));
+ BufferedReader expected = new BufferedReader(new InputStreamReader(getClass()
+ .getResourceAsStream("ivy-extends-merged.xml")));
+ for (String mergeLine = merged.readLine(),
+ expectedLine = expected.readLine();
+ mergeLine != null && expectedLine != null;
+ mergeLine = merged.readLine(),
+ expectedLine = expected.readLine()) {
+
+ mergeLine = mergeLine.trim();
+ expectedLine = expectedLine.trim();
+
+ if (!mergeLine.startsWith("= 0) {
+ mergeLine = mergeLine.replaceFirst("\\s?publication=\"\\d+\"", "");
+ }
+ //discard whitespace-only lines
+ if (!(mergeLine.trim().equals("") && expectedLine.trim().equals(""))) {
+ assertEquals("published descriptor matches at line[" + lineNo + "]", expectedLine, mergeLine);
+ }
+
+ ++lineNo;
+ }
+ }
+
+ public void testMinimalMerge() throws IOException, ParseException {
+ //publish the parent descriptor first, so that it can be found while
+ //we are reading the child descriptor.
+ project.setProperty("ivy.dep.file", "test/java/org/apache/ivy/ant/ivy-multiconf.xml");
+ IvyResolve res = new IvyResolve();
+ res.setProject(project);
+ res.execute();
+
+ IvyPublish pubParent = new IvyPublish();
+ pubParent.setProject(project);
+ pubParent.setResolver("1");
+ pubParent.setPubrevision("1.0");
+ File art = new File("build/test/publish/resolve-simple-1.0.jar");
+ FileUtil.copy(new File("test/repositories/1/org1/mod1.1/jars/mod1.1-1.0.jar"), art, null);
+ pubParent.execute();
+
+ //update=true implies merge=true
+ project.setProperty("ivy.dep.file", "test/java/org/apache/ivy/ant/ivy-extends-minimal.xml");
+ publish.setResolver("1");
+ publish.setUpdate(true);
+ publish.setOrganisation("apache");
+ publish.setModule("resolve-extends");
+ publish.setRevision("1.0");
+ publish.setPubrevision("1.2");
+ publish.setStatus("release");
+ publish.addArtifactspattern("test/java/org/apache/ivy/ant/ivy-extends-minimal.xml");
+ publish.execute();
+
+ // should have published the files with "1" resolver
+ File published = new File("test/repositories/1/apache/resolve-minimal/ivys/ivy-1.2.xml");
+ assertTrue(published.exists());
+
+ // do a text compare, since we want to test comments as well as structure.
+ // we could do a better job of this with xmlunit
+
+ int lineNo = 1;
+
+ BufferedReader merged = new BufferedReader(new FileReader(published));
+ BufferedReader expected = new BufferedReader(new InputStreamReader(getClass()
+ .getResourceAsStream("ivy-extends-minimal-merged.xml")));
+ for (String mergeLine = merged.readLine(),
+ expectedLine = expected.readLine();
+ mergeLine != null && expectedLine != null;
+ mergeLine = merged.readLine(),
+ expectedLine = expected.readLine()) {
+
+ //strip timestamps for the comparison
+ if (mergeLine.indexOf("= 0) {
+ mergeLine = mergeLine.replaceFirst("\\s?publication=\"\\d+\"", "");
+ }
+ //discard whitespace-only lines
+ if (!(mergeLine.trim().equals("") && expectedLine.trim().equals(""))) {
+ assertEquals("published descriptor matches at line[" + lineNo + "]", expectedLine, mergeLine);
+ }
+
+ ++lineNo;
+ }
+ }
+
public void testSimple() throws Exception {
project.setProperty("ivy.dep.file", "test/java/org/apache/ivy/ant/ivy-multiconf.xml");
IvyResolve res = new IvyResolve();
Index: test/java/org/apache/ivy/ant/ivy-extends-merged.xml
===================================================================
--- test/java/org/apache/ivy/ant/ivy-extends-merged.xml (revision 0)
+++ test/java/org/apache/ivy/ant/ivy-extends-merged.xml (revision 0)
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+ Demonstrates configuration-specific dependencies
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
Index: test/java/org/apache/ivy/ant/ivy-extends-minimal-merged.xml
===================================================================
--- test/java/org/apache/ivy/ant/ivy-extends-minimal-merged.xml (revision 0)
+++ test/java/org/apache/ivy/ant/ivy-extends-minimal-merged.xml (revision 0)
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+ Demonstrates configuration-specific dependencies
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
Index: test/java/org/apache/ivy/ant/ivy-extends-minimal.xml
===================================================================
--- test/java/org/apache/ivy/ant/ivy-extends-minimal.xml (revision 0)
+++ test/java/org/apache/ivy/ant/ivy-extends-minimal.xml (revision 0)
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
Index: test/java/org/apache/ivy/ant/ivy-extends-multiconf.xml
===================================================================
--- test/java/org/apache/ivy/ant/ivy-extends-multiconf.xml (revision 0)
+++ test/java/org/apache/ivy/ant/ivy-extends-multiconf.xml (revision 0)
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
Index: test/java/org/apache/ivy/ant/ivy-multiconf.xml
===================================================================
--- test/java/org/apache/ivy/ant/ivy-multiconf.xml (revision 920176)
+++ test/java/org/apache/ivy/ant/ivy-multiconf.xml (working copy)
@@ -21,7 +21,9 @@
module="resolve-simple"
revision="1.0"
status="release"
- />
+ >
+ Demonstrates configuration-specific dependencies
+
Index: test/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorParserTest.java
===================================================================
--- test/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorParserTest.java (revision 920176)
+++ test/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorParserTest.java (working copy)
@@ -31,7 +31,6 @@
import org.apache.ivy.core.module.descriptor.Configuration;
import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
-import org.apache.ivy.core.module.descriptor.DependencyDescriptorMediator;
import org.apache.ivy.core.module.descriptor.ExcludeRule;
import org.apache.ivy.core.module.descriptor.License;
import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
@@ -46,6 +45,8 @@
import org.apache.ivy.plugins.matcher.GlobPatternMatcher;
import org.apache.ivy.plugins.matcher.PatternMatcher;
import org.apache.ivy.plugins.parser.AbstractModuleDescriptorParserTester;
+import org.apache.ivy.plugins.resolver.FileSystemResolver;
+import org.apache.ivy.util.FileUtil;
import org.apache.ivy.util.XMLHelper;
public class XmlModuleDescriptorParserTest extends AbstractModuleDescriptorParserTester {
@@ -55,6 +56,8 @@
super.setUp();
this.settings = new IvySettings();
+ //prevent test from polluting local cache
+ settings.setDefaultCache(new File("build/cache"));
}
public void testSimple() throws Exception {
@@ -78,7 +81,7 @@
assertNotNull(md.getDependencies());
assertEquals(0, md.getDependencies().length);
}
-
+
public void testNamespaces() throws Exception {
ModuleDescriptor md = XmlModuleDescriptorParser.getInstance().parseDescriptor(settings,
getClass().getResource("test-namespaces.xml"), true);
@@ -1018,4 +1021,333 @@
// expected
}
}
+
+ public void testExtendsAll() throws Exception {
+ //default extends type is 'all' when no extendsType attribute is specified.
+ ModuleDescriptor md = XmlModuleDescriptorParser.getInstance().parseDescriptor(settings,
+ getClass().getResource("test-extends-all.xml"), true);
+ assertNotNull(md);
+
+ assertEquals("myorg", md.getModuleRevisionId().getOrganisation());
+ assertEquals("mymodule", md.getModuleRevisionId().getName());
+ assertEquals(Ivy.getWorkingRevision(), md.getModuleRevisionId().getRevision());
+ assertEquals("integration", md.getStatus());
+
+ //verify that the parent description was merged.
+ assertEquals("Parent module description.", md.getDescription());
+
+ //verify that the parent and child configurations were merged together.
+ final Configuration[] expectedConfs = { new Configuration("default"),
+ new Configuration("conf1"), new Configuration("conf2") };
+ assertNotNull(md.getConfigurations());
+ assertEquals(Arrays.asList(expectedConfs), Arrays
+ .asList(md.getConfigurations()));
+
+ //verify parent and child dependencies were merged together.
+ DependencyDescriptor[] deps = md.getDependencies();
+ assertNotNull(deps);
+ assertEquals(2, deps.length);
+
+ assertEquals(Arrays.asList(new String[]{ "default" }),
+ Arrays.asList(deps[0].getModuleConfigurations()));
+ ModuleRevisionId dep = deps[0].getDependencyRevisionId();
+ assertEquals("myorg", dep.getModuleId().getOrganisation());
+ assertEquals("mymodule1", dep.getModuleId().getName());
+ assertEquals("1.0", dep.getRevision());
+
+ assertEquals(Arrays.asList(new String[]{ "conf1", "conf2" }),
+ Arrays.asList(deps[1].getModuleConfigurations()));
+ dep = deps[1].getDependencyRevisionId();
+ assertEquals("myorg", dep.getModuleId().getOrganisation());
+ assertEquals("mymodule2", dep.getModuleId().getName());
+ assertEquals("2.0", dep.getRevision());
+
+ //verify only child publications are present
+ Artifact[] artifacts = md.getAllArtifacts();
+ assertNotNull(artifacts);
+ assertEquals(1, artifacts.length);
+ assertEquals("mymodule", artifacts[0].getName());
+ assertEquals("jar", artifacts[0].getType());
+ }
+
+ public void testExtendsDependencies() throws Exception {
+ //descriptor specifies that only parent dependencies should be included
+ ModuleDescriptor md = XmlModuleDescriptorParser.getInstance().parseDescriptor(settings,
+ getClass().getResource("test-extends-dependencies.xml"), true);
+ assertNotNull(md);
+
+ assertEquals("myorg", md.getModuleRevisionId().getOrganisation());
+ assertEquals("mymodule", md.getModuleRevisionId().getName());
+ assertEquals(Ivy.getWorkingRevision(), md.getModuleRevisionId().getRevision());
+ assertEquals("integration", md.getStatus());
+
+ //verify that the parent description was ignored.
+ assertEquals("", md.getDescription());
+
+ //verify that the parent configurations were ignored.
+ final Configuration[] expectedConfs = { new Configuration("default") };
+ assertNotNull(md.getConfigurations());
+ assertEquals(Arrays.asList(expectedConfs), Arrays
+ .asList(md.getConfigurations()));
+
+ //verify parent dependencies were merged.
+ DependencyDescriptor[] deps = md.getDependencies();
+ assertNotNull(deps);
+ assertEquals(2, deps.length);
+
+ assertEquals(Arrays.asList(new String[]{ "default" }),
+ Arrays.asList(deps[0].getModuleConfigurations()));
+ ModuleRevisionId dep = deps[0].getDependencyRevisionId();
+ assertEquals("myorg", dep.getModuleId().getOrganisation());
+ assertEquals("mymodule1", dep.getModuleId().getName());
+ assertEquals("1.0", dep.getRevision());
+
+ assertEquals(Arrays.asList(new String[]{ "default" }),
+ Arrays.asList(deps[1].getModuleConfigurations()));
+ dep = deps[1].getDependencyRevisionId();
+ assertEquals("myorg", dep.getModuleId().getOrganisation());
+ assertEquals("mymodule2", dep.getModuleId().getName());
+ assertEquals("2.0", dep.getRevision());
+
+ //verify only child publications are present
+ Artifact[] artifacts = md.getAllArtifacts();
+ assertNotNull(artifacts);
+ assertEquals(1, artifacts.length);
+ assertEquals("mymodule", artifacts[0].getName());
+ assertEquals("jar", artifacts[0].getType());
+ }
+
+ public void testExtendsConfigurations() throws Exception {
+ //descriptor specifies that only parent configurations should be included
+ ModuleDescriptor md = XmlModuleDescriptorParser.getInstance().parseDescriptor(settings,
+ getClass().getResource("test-extends-configurations.xml"), true);
+ assertNotNull(md);
+
+ assertEquals("myorg", md.getModuleRevisionId().getOrganisation());
+ assertEquals("mymodule", md.getModuleRevisionId().getName());
+ assertEquals(Ivy.getWorkingRevision(), md.getModuleRevisionId().getRevision());
+ assertEquals("integration", md.getStatus());
+
+ //verify that the parent description was ignored.
+ assertEquals("", md.getDescription());
+
+ //verify that the parent and child configurations were merged together.
+ final Configuration[] expectedConfs = { new Configuration("default"),
+ new Configuration("conf1"), new Configuration("conf2") };
+ assertNotNull(md.getConfigurations());
+ assertEquals(Arrays.asList(expectedConfs), Arrays
+ .asList(md.getConfigurations()));
+
+ //verify parent dependencies were ignored.
+ DependencyDescriptor[] deps = md.getDependencies();
+ assertNotNull(deps);
+ assertEquals(1, deps.length);
+
+ assertEquals(Arrays.asList(new String[]{ "conf1", "conf2" }),
+ Arrays.asList(deps[0].getModuleConfigurations()));
+ ModuleRevisionId dep = deps[0].getDependencyRevisionId();
+ assertEquals("myorg", dep.getModuleId().getOrganisation());
+ assertEquals("mymodule2", dep.getModuleId().getName());
+ assertEquals("2.0", dep.getRevision());
+
+ //verify only child publications are present
+ Artifact[] artifacts = md.getAllArtifacts();
+ assertNotNull(artifacts);
+ assertEquals(1, artifacts.length);
+ assertEquals("mymodule", artifacts[0].getName());
+ assertEquals("jar", artifacts[0].getType());
+ }
+
+ public void testExtendsDescription() throws Exception {
+ //descriptor specifies that only parent description should be included
+ ModuleDescriptor md = XmlModuleDescriptorParser.getInstance().parseDescriptor(settings,
+ getClass().getResource("test-extends-description.xml"), true);
+ assertNotNull(md);
+
+ assertEquals("myorg", md.getModuleRevisionId().getOrganisation());
+ assertEquals("mymodule", md.getModuleRevisionId().getName());
+ assertEquals(Ivy.getWorkingRevision(), md.getModuleRevisionId().getRevision());
+ assertEquals("integration", md.getStatus());
+
+ //verify that the parent description was merged.
+ assertEquals("Parent module description.", md.getDescription());
+
+ //verify that the parent configurations were ignored.
+ final Configuration[] expectedConfs = { new Configuration("default") };
+ assertNotNull(md.getConfigurations());
+ assertEquals(Arrays.asList(expectedConfs), Arrays
+ .asList(md.getConfigurations()));
+
+ //verify parent dependencies were ignored.
+ DependencyDescriptor[] deps = md.getDependencies();
+ assertNotNull(deps);
+ assertEquals(1, deps.length);
+
+ assertEquals(Arrays.asList(new String[]{ "default" }),
+ Arrays.asList(deps[0].getModuleConfigurations()));
+ ModuleRevisionId dep = deps[0].getDependencyRevisionId();
+ assertEquals("myorg", dep.getModuleId().getOrganisation());
+ assertEquals("mymodule2", dep.getModuleId().getName());
+ assertEquals("2.0", dep.getRevision());
+
+ //verify only child publications are present
+ Artifact[] artifacts = md.getAllArtifacts();
+ assertNotNull(artifacts);
+ assertEquals(1, artifacts.length);
+ assertEquals("mymodule", artifacts[0].getName());
+ assertEquals("jar", artifacts[0].getType());
+ }
+
+ public void testExtendsDescriptionWithOverride() throws Exception {
+ //descriptor specifies that only parent description should be included
+ ModuleDescriptor md = XmlModuleDescriptorParser.getInstance().parseDescriptor(settings,
+ getClass().getResource("test-extends-description-override.xml"), true);
+ assertNotNull(md);
+
+ assertEquals("myorg", md.getModuleRevisionId().getOrganisation());
+ assertEquals("mymodule", md.getModuleRevisionId().getName());
+ assertEquals(Ivy.getWorkingRevision(), md.getModuleRevisionId().getRevision());
+ assertEquals("integration", md.getStatus());
+
+ //child description should always be preferred, even if extendType="description"
+ assertEquals("Child description overrides parent.", md.getDescription());
+
+ //verify that the parent configurations were ignored.
+ final Configuration[] expectedConfs = { new Configuration("default") };
+ assertNotNull(md.getConfigurations());
+ assertEquals(Arrays.asList(expectedConfs), Arrays
+ .asList(md.getConfigurations()));
+
+ //verify parent dependencies were ignored.
+ DependencyDescriptor[] deps = md.getDependencies();
+ assertNotNull(deps);
+ assertEquals(1, deps.length);
+
+ assertEquals(Arrays.asList(new String[]{ "default" }),
+ Arrays.asList(deps[0].getModuleConfigurations()));
+ ModuleRevisionId dep = deps[0].getDependencyRevisionId();
+ assertEquals("myorg", dep.getModuleId().getOrganisation());
+ assertEquals("mymodule2", dep.getModuleId().getName());
+ assertEquals("2.0", dep.getRevision());
+
+ //verify only child publications are present
+ Artifact[] artifacts = md.getAllArtifacts();
+ assertNotNull(artifacts);
+ assertEquals(1, artifacts.length);
+ assertEquals("mymodule", artifacts[0].getName());
+ assertEquals("jar", artifacts[0].getType());
+ }
+
+ public void testExtendsMixed() throws Exception {
+ //descriptor specifies that parent configurations and dependencies should be included
+ ModuleDescriptor md = XmlModuleDescriptorParser.getInstance().parseDescriptor(settings,
+ getClass().getResource("test-extends-mixed.xml"), true);
+ assertNotNull(md);
+
+ assertEquals("myorg", md.getModuleRevisionId().getOrganisation());
+ assertEquals("mymodule", md.getModuleRevisionId().getName());
+ assertEquals(Ivy.getWorkingRevision(), md.getModuleRevisionId().getRevision());
+ assertEquals("integration", md.getStatus());
+
+ //verify that the parent description was ignored.
+ assertEquals("", md.getDescription());
+
+ //verify that the parent and child configurations were merged together.
+ final Configuration[] expectedConfs = { new Configuration("default"),
+ new Configuration("conf1"), new Configuration("conf2") };
+ assertNotNull(md.getConfigurations());
+ assertEquals(Arrays.asList(expectedConfs), Arrays
+ .asList(md.getConfigurations()));
+
+ //verify parent and child dependencies were merged together.
+ DependencyDescriptor[] deps = md.getDependencies();
+ assertNotNull(deps);
+ assertEquals(2, deps.length);
+
+ assertEquals(Arrays.asList(new String[]{ "default" }),
+ Arrays.asList(deps[0].getModuleConfigurations()));
+ ModuleRevisionId dep = deps[0].getDependencyRevisionId();
+ assertEquals("myorg", dep.getModuleId().getOrganisation());
+ assertEquals("mymodule1", dep.getModuleId().getName());
+ assertEquals("1.0", dep.getRevision());
+
+ assertEquals(Arrays.asList(new String[]{ "conf1", "conf2" }),
+ Arrays.asList(deps[1].getModuleConfigurations()));
+ dep = deps[1].getDependencyRevisionId();
+ assertEquals("myorg", dep.getModuleId().getOrganisation());
+ assertEquals("mymodule2", dep.getModuleId().getName());
+ assertEquals("2.0", dep.getRevision());
+
+ //verify only child publications are present
+ Artifact[] artifacts = md.getAllArtifacts();
+ assertNotNull(artifacts);
+ assertEquals(1, artifacts.length);
+ assertEquals("mymodule", artifacts[0].getName());
+ assertEquals("jar", artifacts[0].getType());
+ }
+
+ public void testExtendsCached() throws Exception {
+ //configure a resolver to serve the parent descriptor, so that parse succeeds.
+ File resolveRoot = new File("build/tmp/xmlModuleDescriptorTest");
+ assertTrue(resolveRoot.exists() || resolveRoot.mkdirs());
+
+ FileUtil.copy(getClass().getResource("test-extends-parent.xml"),
+ new File(resolveRoot, "myorg/myparent/ivy.xml"), null);
+
+ FileSystemResolver resolver = new FileSystemResolver();
+ resolver.setSettings(settings);
+ resolver.setName("testExtendsCached");
+ resolver.addIvyPattern(resolveRoot.getAbsolutePath()
+ + "/[organisation]/[module]/[artifact].[ext]");
+
+ settings.addResolver(resolver);
+ settings.setDefaultResolver("testExtendsCached");
+
+ //descriptor extends a module without a location="..." attribute, so resolver lookup
+ //must be performed.
+ ModuleDescriptor md = XmlModuleDescriptorParser.getInstance().parseDescriptor(settings,
+ getClass().getResource("test-extends-cached.xml"), true);
+ assertNotNull(md);
+
+ assertEquals("myorg", md.getModuleRevisionId().getOrganisation());
+ assertEquals("mymodule", md.getModuleRevisionId().getName());
+ assertEquals(Ivy.getWorkingRevision(), md.getModuleRevisionId().getRevision());
+ assertEquals("integration", md.getStatus());
+
+ //verify that the parent description was merged.
+ assertEquals("Parent module description.", md.getDescription());
+
+ //verify that the parent and child configurations were merged together.
+ final Configuration[] expectedConfs = { new Configuration("default"),
+ new Configuration("conf1"), new Configuration("conf2") };
+ assertNotNull(md.getConfigurations());
+ assertEquals(Arrays.asList(expectedConfs), Arrays
+ .asList(md.getConfigurations()));
+
+ //verify parent and child dependencies were merged together.
+ DependencyDescriptor[] deps = md.getDependencies();
+ assertNotNull(deps);
+ assertEquals(2, deps.length);
+
+ assertEquals(Arrays.asList(new String[]{ "default" }),
+ Arrays.asList(deps[0].getModuleConfigurations()));
+ ModuleRevisionId dep = deps[0].getDependencyRevisionId();
+ assertEquals("myorg", dep.getModuleId().getOrganisation());
+ assertEquals("mymodule1", dep.getModuleId().getName());
+ assertEquals("1.0", dep.getRevision());
+
+ assertEquals(Arrays.asList(new String[]{ "conf1", "conf2" }),
+ Arrays.asList(deps[1].getModuleConfigurations()));
+ dep = deps[1].getDependencyRevisionId();
+ assertEquals("myorg", dep.getModuleId().getOrganisation());
+ assertEquals("mymodule2", dep.getModuleId().getName());
+ assertEquals("2.0", dep.getRevision());
+
+ //verify only child publications are present
+ Artifact[] artifacts = md.getAllArtifacts();
+ assertNotNull(artifacts);
+ assertEquals(1, artifacts.length);
+ assertEquals("mymodule", artifacts[0].getName());
+ assertEquals("jar", artifacts[0].getType());
+ }
}
Index: test/java/org/apache/ivy/plugins/parser/xml/test-extends-all.xml
===================================================================
--- test/java/org/apache/ivy/plugins/parser/xml/test-extends-all.xml (revision 0)
+++ test/java/org/apache/ivy/plugins/parser/xml/test-extends-all.xml (revision 0)
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
Index: test/java/org/apache/ivy/plugins/parser/xml/test-extends-cached.xml
===================================================================
--- test/java/org/apache/ivy/plugins/parser/xml/test-extends-cached.xml (revision 0)
+++ test/java/org/apache/ivy/plugins/parser/xml/test-extends-cached.xml (revision 0)
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
Index: test/java/org/apache/ivy/plugins/parser/xml/test-extends-configurations.xml
===================================================================
--- test/java/org/apache/ivy/plugins/parser/xml/test-extends-configurations.xml (revision 0)
+++ test/java/org/apache/ivy/plugins/parser/xml/test-extends-configurations.xml (revision 0)
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
Index: test/java/org/apache/ivy/plugins/parser/xml/test-extends-dependencies.xml
===================================================================
--- test/java/org/apache/ivy/plugins/parser/xml/test-extends-dependencies.xml (revision 0)
+++ test/java/org/apache/ivy/plugins/parser/xml/test-extends-dependencies.xml (revision 0)
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
Index: test/java/org/apache/ivy/plugins/parser/xml/test-extends-description-override.xml
===================================================================
--- test/java/org/apache/ivy/plugins/parser/xml/test-extends-description-override.xml (revision 0)
+++ test/java/org/apache/ivy/plugins/parser/xml/test-extends-description-override.xml (revision 0)
@@ -0,0 +1,31 @@
+
+
+
+
+ Child description overrides parent.
+
+
+
+
+
Index: test/java/org/apache/ivy/plugins/parser/xml/test-extends-description.xml
===================================================================
--- test/java/org/apache/ivy/plugins/parser/xml/test-extends-description.xml (revision 0)
+++ test/java/org/apache/ivy/plugins/parser/xml/test-extends-description.xml (revision 0)
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
Index: test/java/org/apache/ivy/plugins/parser/xml/test-extends-mixed.xml
===================================================================
--- test/java/org/apache/ivy/plugins/parser/xml/test-extends-mixed.xml (revision 0)
+++ test/java/org/apache/ivy/plugins/parser/xml/test-extends-mixed.xml (revision 0)
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
Index: test/java/org/apache/ivy/plugins/parser/xml/test-extends-parent.xml
===================================================================
--- test/java/org/apache/ivy/plugins/parser/xml/test-extends-parent.xml (revision 0)
+++ test/java/org/apache/ivy/plugins/parser/xml/test-extends-parent.xml (revision 0)
@@ -0,0 +1,34 @@
+
+
+
+ Parent module description.
+
+
+
+
+
+
+
+
+