[prev in list] [next in list] [prev in thread] [next in thread] 

List:       ivy-commits
Subject:    svn commit: r542921 - in /incubator/ivy/core/trunk: ./
From:       gscokart () apache ! org
Date:       2007-05-30 19:44:29
Message-ID: 20070530194430.371AE1A981A () eris ! apache ! org
[Download RAW message or body]

Author: gscokart
Date: Wed May 30 12:44:28 2007
New Revision: 542921

URL: http://svn.apache.org/viewvc?view=rev&rev=542921
Log:
IVY-493, IVY-509 : fix buildlist regression and add Circular dependency startegy

Added:
    incubator/ivy/core/trunk/src/java/org/apache/ivy/core/sort/CollectionOfModulesToSort.java \
(with props)  incubator/ivy/core/trunk/src/java/org/apache/ivy/core/sort/DefaultNonMatchingVersionReporter.java \
(with props)  incubator/ivy/core/trunk/src/java/org/apache/ivy/core/sort/ModuleInSort.java \
(with props)  incubator/ivy/core/trunk/src/java/org/apache/ivy/core/sort/NonMatchingVersionReporter.java \
(with props) Modified:
    incubator/ivy/core/trunk/CHANGES.txt
    incubator/ivy/core/trunk/doc/doc/configuration/conf.html
    incubator/ivy/core/trunk/doc/doc/use/buildlist.html
    incubator/ivy/core/trunk/src/java/org/apache/ivy/Ivy.java
    incubator/ivy/core/trunk/src/java/org/apache/ivy/ant/IvyBuildList.java
    incubator/ivy/core/trunk/src/java/org/apache/ivy/core/sort/ModuleDescriptorSorter.java
  incubator/ivy/core/trunk/src/java/org/apache/ivy/core/sort/SortEngine.java
    incubator/ivy/core/trunk/src/java/org/apache/ivy/plugins/circular/CircularDependencyHelper.java
  incubator/ivy/core/trunk/src/java/org/apache/ivy/tools/analyser/JarJarDependencyAnalyser.java
  incubator/ivy/core/trunk/test/java/org/apache/ivy/core/sort/SortTest.java
    incubator/ivy/core/trunk/test/java/org/apache/ivy/util/MockMessageImpl.java

Modified: incubator/ivy/core/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/incubator/ivy/core/trunk/CHANGES.txt?view=diff&rev=542921&r1=542920&r2=542921
 ==============================================================================
--- incubator/ivy/core/trunk/CHANGES.txt (original)
+++ incubator/ivy/core/trunk/CHANGES.txt Wed May 30 12:44:28 2007
@@ -69,6 +69,9 @@
 - FIX: Credentials are shown in build log even if debug is not enabled (IVY-486) \
                (thanks to Gilles Scokart)
 - FIX: Post-Resolve task shouldn't set the 'resolveid' (IVY-489)
 - FIX: build fails without emma code coverage JARs present (IVY-478)
+- FIX: buildlist broken - regression in 2.0.0-alpha1-incubating (IVY-493)
+- FIX: Circular dependency startegy in buildlist (IVY-509)
+- FIX: ivy should stop telling me off twice for ivyconf.xml files (IVY-513)
 
    2.0.0-alpha1-incubating
 =====================================

Modified: incubator/ivy/core/trunk/doc/doc/configuration/conf.html
URL: http://svn.apache.org/viewvc/incubator/ivy/core/trunk/doc/doc/configuration/conf.html?view=diff&rev=542921&r1=542920&r2=542921
 ==============================================================================
--- incubator/ivy/core/trunk/doc/doc/configuration/conf.html (original)
+++ incubator/ivy/core/trunk/doc/doc/configuration/conf.html Wed May 30 12:44:28 2007
@@ -60,7 +60,7 @@
         <td>No, defaults to latest-revision</td></tr>
     <tr><td>defaultBranch</td><td>the default branch to use for all modules, except \
if they have a <a href="../../doc/configuration/module.html"> module specific branch \
setting</a>. <span class="since">since 1.4</span></td>  <td>No, defaults to no \
                default branch</td></tr>
-    <tr><td>circularDependencyStrategy</td><td>the name of the <a \
href="../../doc/concept#circular.html">circular dependency strategy</a> to use <span \
class="since">since 1.4</span></td> +    <tr><td><a \
name="circularDependencyStrategy"></a>circularDependencyStrategy</td><td>the name of \
the <a href="../../doc/concept#circular.html">circular dependency strategy</a> to use \
<span class="since">since 1.4</span></td>  <td>No, defaults to warn</td></tr>
     <tr><td>validate</td><td>Indicates if ivy files should be validated against \
ivy.xsd or not.</td>  <td>No, defaults to true</td></tr>

Modified: incubator/ivy/core/trunk/doc/doc/use/buildlist.html
URL: http://svn.apache.org/viewvc/incubator/ivy/core/trunk/doc/doc/use/buildlist.html?view=diff&rev=542921&r1=542920&r2=542921
 ==============================================================================
--- incubator/ivy/core/trunk/doc/doc/use/buildlist.html (original)
+++ incubator/ivy/core/trunk/doc/doc/use/buildlist.html Wed May 30 12:44:28 2007
@@ -29,6 +29,9 @@
 
 This is particularly useful combined with subant, to build a set of interelated \
projects being sure that a dependency will be built before any module depending on \
it.  
+When the ivy.xml of the modules that you want to order doesn't contains a <a \
href="../ivyfile/info.html">revision</a> numbers, the rev attributes declared in the \
dependency is not used. +When the ivy.xml of the modules that you want to order \
contains a <a href="../ivyfile/info.html">revision</a> numbers, the revision numbers \
are used.    If the revision number doesn't match a dependency description a warning \
is logged and the modules is considered as different modules.   +
 <span class="since">since 1.3</span> A root attribute can also be used to include, \
among all the modules found, only the one that are dependencies (either direct or \
transitive) of a root module. This can also be used with the excluderoot attribute, \
which when set to true will exclude the root itself from the list.  
 <span class="since">since 1.4.1</span> A leaf attribute can also be used to include, \
among all the modules found, only the one that have dependencies (either direct or \
transitive) on a leaf module. This can also be used with the excludeleaf attribute, \
which when set to true will exclude the leaf itself from the list. @@ -36,6 +39,11 @@
 <span class="since">since 1.4</span> The ivy.sorted.modules property is set in the \
ant at the end of the task with a comma separated list of ordered modules. This can \
be useful for debug or information purpose.  
 <span class="since">since 2.0</span> The root and leaf attributes can be a delimited \
list of modules to use as roots.  These modules, and all their dependencies will be \
included in the build list. +
+<span class="since">since 2.0</span> By default, all the modules included in a \
circular dependency are grouped together so that any dependency of any module in the \
loop will apear before the modules in the loop.  This garantee that if there is a \
depedendency path between a module A and a module B (but no dependency path from B to \
A), B will alway apear before A even if A is included in a loop in the provided set \
of modules to sort. +Note that circular dependency can also trigger a failure \
depending on the value configured in the circularDependencyStrategy of your <a \
href="../configuration/conf.html#circularDependencyStrategy">settings</a> +
+
 <table class="ant">
 <thead>
     <tr><th class="ant-att">Attribute</th><th class="ant-desc">Description</th><th \
class="ant-req">Required</th></tr> @@ -52,7 +60,7 @@
     <tr><td>haltonerror</td><td>true to halt the build when an invalid ivy file is \
encountered, false to continue</td><td>No. Defaults to true</td></tr>  \
<tr><td>skipbuildwithoutivy</td><td>true to skip files of the fileset with no \
corresponding ivy file, false otherwise. If false the file with no corresponding ivy \
file will be considered as independent of the other and put at the beginning of the \
built filelist.</td><td>No. Defaults to false</td></tr>  <tr><td>reverse</td><td>true \
to obtain the list in the reverse order, i.e. from the most dependent to the least \
                one</td><td>No. Defaults to default false</td></tr>
-    <tr><td>settingsRef</td><td><span class="since">(since 2.0)</span> A reference \
to the ivy settings that must be used by this task</td><td>No, 'ivy.instance' is \
taken by default.</td></tr> +    <tr><td>settingsRef</td><td><span \
class="since">since 2.0</span> A reference to the ivy settings that must be used by \
this task</td><td>No, 'ivy.instance' is taken by default.</td></tr>  </tbody>
 </table>
 

Modified: incubator/ivy/core/trunk/src/java/org/apache/ivy/Ivy.java
URL: http://svn.apache.org/viewvc/incubator/ivy/core/trunk/src/java/org/apache/ivy/Ivy.java?view=diff&rev=542921&r1=542920&r2=542921
 ==============================================================================
--- incubator/ivy/core/trunk/src/java/org/apache/ivy/Ivy.java (original)
+++ incubator/ivy/core/trunk/src/java/org/apache/ivy/Ivy.java Wed May 30 12:44:28 \
2007 @@ -51,6 +51,7 @@
 import org.apache.ivy.core.search.RevisionEntry;
 import org.apache.ivy.core.search.SearchEngine;
 import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.core.sort.NonMatchingVersionReporter;
 import org.apache.ivy.core.sort.SortEngine;
 import org.apache.ivy.plugins.matcher.PatternMatcher;
 import org.apache.ivy.plugins.repository.TransferEvent;
@@ -332,6 +333,10 @@
         return _sortEngine.sortModuleDescriptors(moduleDescriptors);   
     }
     
+    public List sortModuleDescriptors(Collection moduleDescriptors, \
NonMatchingVersionReporter nonMatchingVersionReporter) { +    	return \
_sortEngine.sortModuleDescriptors(moduleDescriptors , nonMatchingVersionReporter); \
+	} +    
     /////////////////////////////////////////////////////////////////////////
     //                         SEARCH
     /////////////////////////////////////////////////////////////////////////
@@ -562,5 +567,7 @@
 	public void setSettings(IvySettings settings) {
 		_settings = settings;
 	}
+
+	
 
 }

Modified: incubator/ivy/core/trunk/src/java/org/apache/ivy/ant/IvyBuildList.java
URL: http://svn.apache.org/viewvc/incubator/ivy/core/trunk/src/java/org/apache/ivy/ant/IvyBuildList.java?view=diff&rev=542921&r1=542920&r2=542921
 ==============================================================================
--- incubator/ivy/core/trunk/src/java/org/apache/ivy/ant/IvyBuildList.java (original)
+++ incubator/ivy/core/trunk/src/java/org/apache/ivy/ant/IvyBuildList.java Wed May 30 \
12:44:28 2007 @@ -47,86 +47,85 @@
 
 /**
  * Creates an ant filelist of files (usually build.xml) ordered according to the \
                dependencies declared in ivy files.
- * 
  */
 public class IvyBuildList extends IvyTask {
-    private List _buildFiles = new ArrayList(); // List (FileSet)
-    private String _reference;
-    private boolean _haltOnError = true;
-    private boolean _skipBuildWithoutIvy = false;
-    private boolean _reverse = false;
-    private String _ivyFilePath;
-    private String _root = "*";
-    private boolean _excludeRoot = false;
-    private String _leaf = "*";
-    private String _delimiter = ",";
-    private boolean _excludeLeaf = false;
+    private List buildFileSets = new ArrayList(); // List (FileSet)
+    private String reference;
+    private boolean haltOnError = true;
+    private boolean skipBuildWithoutIvy = false;
+    private boolean reverse = false;
+    private String ivyFilePath;
+    private String root = "*";
+    private boolean excludeRoot = false;
+    private String leaf = "*";
+    private String delimiter = ",";
+    private boolean excludeLeaf = false;
 
 
     public void addFileset(FileSet buildFiles) {
-        _buildFiles.add(buildFiles);
+        buildFileSets.add(buildFiles);
     }
 
     public String getReference() {
-        return _reference;
+        return reference;
     }
 
     public void setReference(String reference) {
-        _reference = reference;
+        this.reference = reference;
     }
 
     public String getRoot() {
-        return _root;
+        return root;
     }
 
     public void setRoot(String root) {
-        _root = root;
+        this.root = root;
     }
 
     public boolean isExcludeRoot() {
-        return _excludeRoot;
+        return excludeRoot;
     }
 
     public void setExcludeRoot(boolean root) {
-        _excludeRoot = root;
+        excludeRoot = root;
     }
 
 	public String getLeaf() {
-		return _leaf;
+		return leaf;
 	}
 
 	public void setLeaf(String leaf) {
-		_leaf = leaf;
+		this.leaf = leaf;
 	}
 
 	public boolean isExcludeLeaf() {
-		return _excludeLeaf;
+		return excludeLeaf;
 	}
 
 	public void setExcludeLeaf(boolean excludeLeaf) {
-		_excludeLeaf = excludeLeaf;
+		this.excludeLeaf = excludeLeaf;
 	}
 
 	public String getDelimiter() {
-		return _delimiter;
+		return delimiter;
 	}
 	
 	public void setDelimiter(String delimiter) {
-		_delimiter = delimiter;
+		this.delimiter = delimiter;
 	}
 	
     public void doExecute() throws BuildException {
-        if (_reference == null) {
+        if (reference == null) {
             throw new BuildException("reference should be provided in ivy build \
list");  }
-        if (_buildFiles.isEmpty()) {
+        if (buildFileSets.isEmpty()) {
             throw new BuildException("at least one nested fileset should be provided \
in ivy build list");  }
 
         Ivy ivy = getIvyInstance();
         IvySettings settings = ivy.getSettings();
         
-		_ivyFilePath = getProperty(_ivyFilePath, settings, "ivy.buildlist.ivyfilepath");
+		ivyFilePath = getProperty(ivyFilePath, settings, "ivy.buildlist.ivyfilepath");
 
         Path path = new Path(getProject());
 
@@ -135,22 +134,22 @@
         List independent = new ArrayList();
 
         Set rootModuleNames = new LinkedHashSet();
-        if (!"*".equals(_root)) {
-        	StringTokenizer st = new StringTokenizer(_root, _delimiter);
+        if (!"*".equals(root)) {
+        	StringTokenizer st = new StringTokenizer(root, delimiter);
         	while (st.hasMoreTokens()) {
         		rootModuleNames.add(st.nextToken());
         	}
         }
         
         Set leafModuleNames = new LinkedHashSet();
-        if (! "*".equals(_leaf)) {
-        	StringTokenizer st = new StringTokenizer(_leaf, _delimiter);
+        if (! "*".equals(leaf)) {
+        	StringTokenizer st = new StringTokenizer(leaf, delimiter);
         	while (st.hasMoreTokens()) {
         		leafModuleNames.add(st.nextToken());
         	}
         }
         
-        for (ListIterator iter = _buildFiles.listIterator(); iter.hasNext();) {
+        for (ListIterator iter = buildFileSets.listIterator(); iter.hasNext();) {
             FileSet fs = (FileSet)iter.next();
             DirectoryScanner ds = fs.getDirectoryScanner(getProject());
             String[] builds = ds.getIncludedFiles();
@@ -158,7 +157,7 @@
                 File buildFile = new File(ds.getBasedir(), builds[i]);
                 File ivyFile = getIvyFileFor(buildFile);
                 if (!ivyFile.exists()) {
-                    if (_skipBuildWithoutIvy) {
+                    if (skipBuildWithoutIvy) {
                         Message.debug("skipping "+buildFile+": ivy file "+ivyFile+" \
doesn't exist");  } else {
                         Message.verbose("no ivy file for "+buildFile+": \
ivyfile="+ivyFile+": adding it at the beginning of the path"); @@ -170,8 +169,9 @@
                         ModuleDescriptor md = \
ModuleDescriptorParserRegistry.getInstance().parseDescriptor(settings, \
ivyFile.toURL(), doValidate(settings));  buildFiles.put(md, buildFile);
                         mdsMap.put(md.getModuleRevisionId().getModuleId().getName(), \
md); +                        Message.debug("Add " + \
md.getModuleRevisionId().getModuleId());  } catch (Exception ex) {
-                        if (_haltOnError) {
+                        if (haltOnError) {
                             throw new BuildException("impossible to parse ivy file \
for "+buildFile+": ivyfile="+ivyFile+" exception="+ex, ex);  } else {
                             Message.warn("impossible to parse ivy file for \
"+buildFile+": ivyfile="+ivyFile+" exception="+ex.getMessage()); @@ -257,7 +257,7 @@
         	ModuleDescriptor rootmd = (ModuleDescriptor) it.next();
             processFilterNodeFromRoot(rootmd, toKeep, moduleIdMap);
             // With the excluderoot attribute set to true, take the rootmd out of \
                the toKeep set.
-            if (_excludeRoot) {
+            if (excludeRoot) {
                 Message.verbose("Excluded module " + \
rootmd.getModuleRevisionId().getModuleId().getName());  toKeep.remove(rootmd);
             }
@@ -315,7 +315,7 @@
         while (it.hasNext()) {
         	ModuleDescriptor leafmd = (ModuleDescriptor) it.next();
             // With the excludeleaf attribute set to true, take the rootmd out of \
                the toKeep set.
-            if (_excludeLeaf) {
+            if (excludeLeaf) {
             	Message.verbose("Excluded module " + \
leafmd.getModuleRevisionId().getModuleId().getName());  } else {
             	toKeep.add(leafmd);
@@ -365,40 +365,40 @@
     }
 
     private File getIvyFileFor(File buildFile) {
-        return new File(buildFile.getParentFile(), _ivyFilePath);
+        return new File(buildFile.getParentFile(), ivyFilePath);
     }
 
     public boolean isHaltonerror() {
-        return _haltOnError;
+        return haltOnError;
     }
 
     public void setHaltonerror(boolean haltOnError) {
-        _haltOnError = haltOnError;
+        this.haltOnError = haltOnError;
     }
 
     public String getIvyfilepath() {
-        return _ivyFilePath;
+        return ivyFilePath;
     }
 
     public void setIvyfilepath(String ivyFilePath) {
-        _ivyFilePath = ivyFilePath;
+        this.ivyFilePath = ivyFilePath;
     }
 
     public boolean isSkipbuildwithoutivy() {
-        return _skipBuildWithoutIvy;
+        return skipBuildWithoutIvy;
     }
 
     public void setSkipbuildwithoutivy(boolean skipBuildFilesWithoutIvy) {
-        _skipBuildWithoutIvy = skipBuildFilesWithoutIvy;
+        this.skipBuildWithoutIvy = skipBuildFilesWithoutIvy;
     }
 
     public boolean isReverse() {
-        return _reverse;
+        return reverse;
     }
 
 
     public void setReverse(boolean reverse) {
-        _reverse = reverse;
+        this.reverse = reverse;
     }
 
 }

Added: incubator/ivy/core/trunk/src/java/org/apache/ivy/core/sort/CollectionOfModulesToSort.java
                
URL: http://svn.apache.org/viewvc/incubator/ivy/core/trunk/src/java/org/apache/ivy/core/sort/CollectionOfModulesToSort.java?view=auto&rev=542921
 ==============================================================================
--- incubator/ivy/core/trunk/src/java/org/apache/ivy/core/sort/CollectionOfModulesToSort.java \
                (added)
+++ incubator/ivy/core/trunk/src/java/org/apache/ivy/core/sort/CollectionOfModulesToSort.java \
Wed May 30 12:44:28 2007 @@ -0,0 +1,105 @@
+/*
+ *  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.sort;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.plugins.version.VersionMatcher;
+
+/**
+ * Wrap a collection of descriptores wrapped themself in ModuleInSort elements.  It \
contains some + * dedicated function to retrieve module descriptors based on \
dependencies descriptors.<br> + * <i>This class is designed to be used internally by \
the ModuleDescriptorSorter.</i> + */
+class CollectionOfModulesToSort implements Iterable {
+
+	private final List moduleDescriptors;	//List<ModuleInSort>
+	private final VersionMatcher versionMatcher;
+	private final Map modulesByModuleId;	//Map<ModuleId , Collection<ModuleInSort>
+	private final NonMatchingVersionReporter nonMatchingVersionReporter;
+	
+	/**
+	 * @param modulesToSort The collection of ModuleDescriptor to sort 
+	 * @param matcher The matcher to used to check if dependencyDescriptor match a \
module in this collection +	 * @param nonMatchingVersionReporter 
+	 */
+	public CollectionOfModulesToSort(Collection modulesToSort, VersionMatcher matcher, \
NonMatchingVersionReporter nonMatchingVersionReporter) { +		this.versionMatcher = \
matcher; +		this.nonMatchingVersionReporter = nonMatchingVersionReporter;
+		this.modulesByModuleId = new HashMap();
+		moduleDescriptors = new ArrayList(modulesToSort.size());
+		for (Iterator it = modulesToSort.iterator(); it.hasNext();) {
+			ModuleDescriptor md = (ModuleDescriptor) it.next();
+			ModuleInSort mdInSort = new ModuleInSort(md);
+			moduleDescriptors.add(mdInSort);
+			addToModulesByModuleId(md, mdInSort);
+		}
+	}
+
+
+	private void addToModulesByModuleId(ModuleDescriptor md, ModuleInSort mdInSort) {
+		ModuleId mdId = md.getModuleRevisionId().getModuleId(); 
+		List mdInSortAsList = new LinkedList();
+		mdInSortAsList.add(mdInSort);
+		List previousList = (List) modulesByModuleId.put(mdId, mdInSortAsList);
+		if (previousList!=null) {
+			mdInSortAsList.addAll(previousList);
+		}
+	}
+	
+	
+	public Iterator iterator() {
+		return moduleDescriptors.iterator();
+	}
+
+
+	public int size() {
+		return moduleDescriptors.size();
+	}
+	
+	
+	/**
+	 * Find a matching module descriptor in the list of module to sort.
+     * @param descriptor
+     * @return a ModuleDescriptor from the collection of module descriptors to sort.
+     * If none exists returns null.
+     */
+    public ModuleInSort getModuleDescriptorDependency(DependencyDescriptor \
descriptor) { +    	Collection modulesOfSameId = (Collection) \
modulesByModuleId.get(descriptor.getDependencyId()); +    	if (modulesOfSameId==null) \
return null; +    	for (Iterator it = modulesOfSameId.iterator(); it.hasNext();) {
+			ModuleInSort mdInSort = (ModuleInSort) it.next();
+			if (mdInSort.match(descriptor, versionMatcher)) {
+				return mdInSort;
+            } else {
+            	nonMatchingVersionReporter.reportNonMatchingVersion(descriptor, \
mdInSort.getSortedModuleDescriptor()); +            }
+        }
+        return null;
+    }
+
+}

Propchange: incubator/ivy/core/trunk/src/java/org/apache/ivy/core/sort/CollectionOfModulesToSort.java
                
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/ivy/core/trunk/src/java/org/apache/ivy/core/sort/DefaultNonMatchingVersionReporter.java
                
URL: http://svn.apache.org/viewvc/incubator/ivy/core/trunk/src/java/org/apache/ivy/core/sort/DefaultNonMatchingVersionReporter.java?view=auto&rev=542921
 ==============================================================================
--- incubator/ivy/core/trunk/src/java/org/apache/ivy/core/sort/DefaultNonMatchingVersionReporter.java \
                (added)
+++ incubator/ivy/core/trunk/src/java/org/apache/ivy/core/sort/DefaultNonMatchingVersionReporter.java \
Wed May 30 12:44:28 2007 @@ -0,0 +1,48 @@
+/*
+ *  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.sort;
+
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.util.Message;
+
+/**
+ * A default implementation of the reporter used in the sort.  The reporting is \
isolated here to make + * it easier to test, and to have a place where adding \
different type of reporting (silent, warning, exceptions)  + */
+public class DefaultNonMatchingVersionReporter implements NonMatchingVersionReporter \
{ +
+	public void reportNonMatchingVersion(DependencyDescriptor descriptor, \
ModuleDescriptor md) { +    	ModuleRevisionId dependencyRevisionId = \
descriptor.getDependencyRevisionId(); +    	ModuleRevisionId parentRevisionId = \
descriptor.getParentRevisionId(); +    	if (parentRevisionId==null) {
+    		//There are some rare case where DependencyDescriptor have no parent.  
+    		//This is should not be used in the SortEngine, but if it is, we show a decent \
trace. +    		Message.warn("Non matching revision detected.  Dependency " + \
dependencyRevisionId + +        			" doesn't match " + md);
+    	} else {
+    		ModuleId parentModuleId = parentRevisionId.getModuleId(); 
+    		Message.warn("Non matching revision detected.  " + parentModuleId + " depends \
on "  +    				+ dependencyRevisionId + ", doesn't match " + md);
+    	}
+
+	}
+
+}

Propchange: incubator/ivy/core/trunk/src/java/org/apache/ivy/core/sort/DefaultNonMatchingVersionReporter.java
                
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: incubator/ivy/core/trunk/src/java/org/apache/ivy/core/sort/ModuleDescriptorSorter.java
                
URL: http://svn.apache.org/viewvc/incubator/ivy/core/trunk/src/java/org/apache/ivy/core/sort/ModuleDescriptorSorter.java?view=diff&rev=542921&r1=542920&r2=542921
 ==============================================================================
--- incubator/ivy/core/trunk/src/java/org/apache/ivy/core/sort/ModuleDescriptorSorter.java \
                (original)
+++ incubator/ivy/core/trunk/src/java/org/apache/ivy/core/sort/ModuleDescriptorSorter.java \
Wed May 30 12:44:28 2007 @@ -21,30 +21,31 @@
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Stack;
 
 import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
-import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
 import org.apache.ivy.plugins.circular.CircularDependencyException;
-import org.apache.ivy.plugins.circular.CircularDependencyHelper;
+import org.apache.ivy.plugins.circular.CircularDependencyStrategy;
 import org.apache.ivy.plugins.version.VersionMatcher;
 import org.apache.ivy.util.Message;
 
 
 /**
- * Inner helper class for sorting ModuleDescriptors.
- *
+ * Inner helper class for sorting ModuleDescriptors.<br>
+ * ModuleDescriptorSorter use CollectionOfModulesToSort to find the dependencies of \
the modules, and use ModuleInSort + * to store some temporary values attached to the \
modules to sort. + * @see ModuleInSort
+ * @see CollectionOfModulesToSort
  */
 public class ModuleDescriptorSorter {
-    
-    
-    private final Collection moduleDescriptors;
-    private final Iterator moduleDescriptorsIterator;
+        
+    private final CollectionOfModulesToSort moduleDescriptors;
     private final List sorted = new LinkedList();
+	private final CircularDependencyStrategy circularDepStrategy;
+      
     
-    public ModuleDescriptorSorter(Collection moduleDescriptors) {
-        this.moduleDescriptors=moduleDescriptors;
-        moduleDescriptorsIterator = new LinkedList(moduleDescriptors).iterator();
+    public ModuleDescriptorSorter(Collection modulesDescriptorsToSort,VersionMatcher \
matcher, NonMatchingVersionReporter nonMatchingVersionReporter, \
CircularDependencyStrategy circularDepStrategy) { +    	this.circularDepStrategy = \
circularDepStrategy; +		moduleDescriptors = new \
CollectionOfModulesToSort(modulesDescriptorsToSort, matcher , \
nonMatchingVersionReporter);  }
     
     /**
@@ -52,9 +53,12 @@
      * @return sorted module
      * @throws CircularDependencyException
      */
-    public List sortModuleDescriptors(VersionMatcher matcher) throws \
                CircularDependencyException {
-        while (moduleDescriptorsIterator.hasNext()) {
-            sortModuleDescriptorsHelp(matcher, \
(ModuleDescriptor)moduleDescriptorsIterator.next(), new Stack()); +    public List \
sortModuleDescriptors() throws CircularDependencyException { +    	Message.debug("Nbr \
of module to sort : " + moduleDescriptors.size()); +    	Iterator \
_moduleDescriptorsIterator = moduleDescriptors.iterator(); +        while \
(_moduleDescriptorsIterator.hasNext()) { +            ModuleInSort next = \
(ModuleInSort)_moduleDescriptorsIterator.next(); +			sortModuleDescriptorsHelp(next, \
next);  }
         return sorted;
     }
@@ -63,52 +67,33 @@
      * If current module has already been added to list, returns,
      * Otherwise invokes sortModuleDescriptorsHelp for all dependencies
      * contained within set of moduleDescriptors.  Then finally adds self
-     * to list of sorted.
+     * to list of sorted.<br/>
+     * When a loop is detected by a recursive call, the moduleDescriptors are not \
added +     * immediately added to the sorted list.  They are added as loop \
dependencies of the root, and will be +     * added to the sorted list only when the \
                root itself will be added. 
      * @param current Current module to add to sorted list.
      * @throws CircularDependencyException
      */
-    private void sortModuleDescriptorsHelp(VersionMatcher matcher, ModuleDescriptor \
current, Stack callStack) throws CircularDependencyException { +    private void \
sortModuleDescriptorsHelp(ModuleInSort current, ModuleInSort caller) throws \
CircularDependencyException {  //if already sorted return
-        if (sorted.contains(current)) {
-            return;
-        }
-        if (callStack.contains(current)) {
-            callStack.add(current);
-            Message.verbose("circular dependency ignored during sort: \
"+CircularDependencyHelper.formatMessage((ModuleDescriptor[]) callStack.toArray(new \
ModuleDescriptor[callStack.size()]))); +        if (current.isSorted()) {
             return;
         }
+		if (current.checkLoop(caller , circularDepStrategy)) {
+			return;
+		}
         DependencyDescriptor [] descriptors = current.getDependencies();
-        ModuleDescriptor moduleDescriptorDependency = null;
-        for (int i = 0; descriptors!=null && i < descriptors.length; i++) {
-            moduleDescriptorDependency = getModuleDescriptorDependency(matcher, \
                descriptors[i]);
-            
-            if (moduleDescriptorDependency != null) {
-                callStack.push(current);
-                sortModuleDescriptorsHelp(matcher, moduleDescriptorDependency, \
                callStack);
-                callStack.pop();
+        Message.debug("Sort dependencies of : " + current.toString() + " / Number of \
dependencies = " + descriptors.length); +        current.setCaller(caller);
+        for (int i = 0; i < descriptors.length; i++) {
+        	ModuleInSort child = \
moduleDescriptors.getModuleDescriptorDependency(descriptors[i]); +            if \
(child != null) { +                sortModuleDescriptorsHelp(child, current);
             }
         }
-        sorted.add(current);
+        current.endOfCall();
+        Message.debug("Sort done for : " + current.toString());
+        current.addToSortedListIfRequired(sorted);
     }
 
-    /**
-     * @param descriptor
-     * @return a ModuleDescriptor from the collection of module descriptors to sort.
-     * If none exists returns null.
-     */
-    private ModuleDescriptor getModuleDescriptorDependency(VersionMatcher matcher, \
                DependencyDescriptor descriptor) {
-        Iterator i = moduleDescriptors.iterator();
-        ModuleDescriptor md = null;
-        while (i.hasNext()) {
-            md = (ModuleDescriptor) i.next();
-            if (descriptor.getDependencyId().equals(md.getModuleRevisionId().getModuleId())) \
                {
-                if (md.getResolvedModuleRevisionId().getRevision() == null) {
-                    return md;
-                } else if (matcher.accept(descriptor.getDependencyRevisionId(), md)) \
                {
-                    return md;
-                }
-            }
-        }
-        return null;
-    }
 }

Added: incubator/ivy/core/trunk/src/java/org/apache/ivy/core/sort/ModuleInSort.java
URL: http://svn.apache.org/viewvc/incubator/ivy/core/trunk/src/java/org/apache/ivy/core/sort/ModuleInSort.java?view=auto&rev=542921
 ==============================================================================
--- incubator/ivy/core/trunk/src/java/org/apache/ivy/core/sort/ModuleInSort.java \
                (added)
+++ incubator/ivy/core/trunk/src/java/org/apache/ivy/core/sort/ModuleInSort.java Wed \
May 30 12:44:28 2007 @@ -0,0 +1,164 @@
+/*
+ *  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.sort;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.plugins.circular.CircularDependencyHelper;
+import org.apache.ivy.plugins.circular.CircularDependencyStrategy;
+import org.apache.ivy.plugins.version.VersionMatcher;
+import org.apache.ivy.util.Message;
+
+/** 
+ * Decorates a ModuleDescriptor with some attributes used during the sort.  Thus \
every instance of a ModuleInSort can be used in  + * only one ModuleDescriptorSorter \
at a time.<br> + * The added fields are : <br>
+ * <ul>
+ * <li><code>isSorted</code> : is true iff this module has already been added to the \
sorted list.</li> + * <li><code>loopElements</code> : When the module is the root of \
a loop (=the first element of a loop met during the sort),  + * \
<code>loopElements</code> contains all ModuleInSort of the loop (excluding the root \
itself.</li> + * <li><code>isLoopIntermediateElement</code> : When a loop is \
detected, all modules included in the loop  + * 		(except the root) have \
<code>isLoopIntermediateElement</code> set to true.</li> + * <li><code>caller</code> \
: During the sort, we traverse recursively the graph.  When doing that, caller point \
to the parent element.  + */
+class ModuleInSort {
+	
+	private final ModuleDescriptor module;
+	private boolean isSorted = false; 
+	private List loopElements = new LinkedList();
+	private boolean isLoopIntermediateElement = false; 
+	private ModuleInSort caller;
+	
+	public ModuleInSort(ModuleDescriptor moduleToSort) {
+		module = moduleToSort;
+	}
+	
+	public boolean isInLoop() {
+		return isLoopIntermediateElement;
+	}
+
+	public boolean isSorted() {
+		if (isSorted) {
+			Message.debug("Module descriptor already sorted : " + \
module.getModuleRevisionId().toString()); +			return true;
+		} else {
+			return false;
+		}
+	}
+	
+	public void setCaller(ModuleInSort caller) {
+		this.caller = caller;
+	}
+
+	public void endOfCall() {
+		caller = null;
+	}
+
+	/** 
+	 * Check if a adding this element as a dependency of caller will introduce a \
circular dependency. +	 * If it is, all the elements of the loop are flaged as \
'loopIntermediateElement', and the loopElements of this module +	 * (which is the \
root of the loop) is updated.  The depStrategy is invoked on order to report a \
correct circular loop message.  +	 * @param futurCaller
+	 * @param depStrategy
+	 * @return true if a loop is detected.
+	 */
+	public boolean checkLoop(ModuleInSort futurCaller, CircularDependencyStrategy \
depStrategy) { +		if (caller!=null) {        	
+	        LinkedList elemOfLoop = new LinkedList();
+	        elemOfLoop.add(this.module.getModuleRevisionId());
+			for(ModuleInSort stackElem = futurCaller ; stackElem!=this ; \
stackElem=stackElem.caller) { \
+				elemOfLoop.add(stackElem.module.getModuleRevisionId()); \
+				stackElem.isLoopIntermediateElement = true; +				loopElements.add(stackElem);
+			}
+			elemOfLoop.add(this.module.getModuleRevisionId());
+			ModuleRevisionId[] mrids = (ModuleRevisionId[]) elemOfLoop.toArray(new \
ModuleRevisionId[elemOfLoop.size()]); \
+			depStrategy.handleCircularDependency(mrids); +	        return true;
+	    } else {
+	    	return false;
+	    }
+	}
+
+	/**
+	 * Add this module to the sorted list except if this module is an intermediary \
element of a loop. +	 * If this module is the 'root' of a loop, then all elements of \
that loops are added before. +	 * @param sorted The list of sorted elements on which \
this module will be added +	 */
+	public void addToSortedListIfRequired(List sorted) {
+		if (!isLoopIntermediateElement) {
+			addToSortList(sorted);
+	    }
+	}
+
+
+	/** 
+	 * Add this module to the sorted list.  
+	 * If current is the 'root' of a loop, then all elements of that loops are added \
before. +	 */
+	private void addToSortList(List sortedList) {
+		for (Iterator it = loopElements.iterator(); it.hasNext();) {
+			ModuleInSort moduleInLoop = (ModuleInSort) it.next();
+			moduleInLoop.addToSortList(sortedList);
+		}
+		if (!this.isSorted()) {
+			sortedList.add(module);
+			this.isSorted = true;
+		}
+	}
+
+	
+	public String toString() {
+		return module.getModuleRevisionId().toString();
+	}
+
+	public DependencyDescriptor[] getDependencies() {
+		return module.getDependencies();
+	}
+
+	
+	
+	/** Log a warning saying that a loop is detected */
+	public static void logLoopWarning(List loopElement) {
+		Message.warn("circular dependency detected during sort: \
"+CircularDependencyHelper.formatMessageFromDescriptors(loopElement)); +	}
+
+
+	/**
+	 * Return true if this module match the DependencyDescriptor with the given \
versionMatcher. +	 * If this module has no version defined, then true is always \
returned.   +	 */
+	public boolean match(DependencyDescriptor descriptor, VersionMatcher \
versionMatcher) { +		ModuleDescriptor md = module;
+		return md.getResolvedModuleRevisionId().getRevision() == null || 
+				md.getResolvedModuleRevisionId().getRevision().equals(Ivy.getWorkingRevision()) \
|| +				versionMatcher.accept(descriptor.getDependencyRevisionId(), md);
+		//Checking md.getResolvedModuleRevisionId().getRevision().equals(Ivy.getWorkingRevision() \
allow to consider any local non resolved ivy.xml +		//as a valid module.
+	}
+	
+	public ModuleDescriptor getSortedModuleDescriptor() {
+		return module;
+	}
+}

Propchange: incubator/ivy/core/trunk/src/java/org/apache/ivy/core/sort/ModuleInSort.java
                
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/ivy/core/trunk/src/java/org/apache/ivy/core/sort/NonMatchingVersionReporter.java
                
URL: http://svn.apache.org/viewvc/incubator/ivy/core/trunk/src/java/org/apache/ivy/core/sort/NonMatchingVersionReporter.java?view=auto&rev=542921
 ==============================================================================
--- incubator/ivy/core/trunk/src/java/org/apache/ivy/core/sort/NonMatchingVersionReporter.java \
                (added)
+++ incubator/ivy/core/trunk/src/java/org/apache/ivy/core/sort/NonMatchingVersionReporter.java \
Wed May 30 12:44:28 2007 @@ -0,0 +1,33 @@
+/*
+ *  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.sort;
+
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+
+public interface NonMatchingVersionReporter {
+
+    /** 
+     * Report to the user that ivy has detected that a module to sort has a \
dependency +	 * on an other module to sort, but the revisions doesn't match.
+	 * @param descriptor The non matching dependency descriptor.
+	 * @param md The module to sort having the corect moduleID but a non matching \
revision +	 */ 
+	public void reportNonMatchingVersion(DependencyDescriptor descriptor, \
ModuleDescriptor md); +	
+}

Propchange: incubator/ivy/core/trunk/src/java/org/apache/ivy/core/sort/NonMatchingVersionReporter.java
                
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: incubator/ivy/core/trunk/src/java/org/apache/ivy/core/sort/SortEngine.java
URL: http://svn.apache.org/viewvc/incubator/ivy/core/trunk/src/java/org/apache/ivy/core/sort/SortEngine.java?view=diff&rev=542921&r1=542920&r2=542921
 ==============================================================================
--- incubator/ivy/core/trunk/src/java/org/apache/ivy/core/sort/SortEngine.java \
                (original)
+++ incubator/ivy/core/trunk/src/java/org/apache/ivy/core/sort/SortEngine.java Wed \
May 30 12:44:28 2007 @@ -80,7 +80,14 @@
      * @throws CircularDependencyException if a circular dependency exists
      */
     public List sortModuleDescriptors(Collection moduleDescriptors) throws \
                CircularDependencyException {
-        return new ModuleDescriptorSorter(moduleDescriptors).sortModuleDescriptors(_settings.getVersionMatcher()); \
 +		return sortModuleDescriptors(moduleDescriptors , new \
DefaultNonMatchingVersionReporter());     }
+
+
+	public List sortModuleDescriptors(Collection moduleDescriptors, \
NonMatchingVersionReporter nonMatchingVersionReporter) { +		ModuleDescriptorSorter \
sorter = new ModuleDescriptorSorter(moduleDescriptors,  +        \
_settings.getVersionMatcher(), nonMatchingVersionReporter , \
_settings.getCircularDependencyStrategy()); +		return sorter.sortModuleDescriptors();
+	}
 
 }

Modified: incubator/ivy/core/trunk/src/java/org/apache/ivy/plugins/circular/CircularDependencyHelper.java
                
URL: http://svn.apache.org/viewvc/incubator/ivy/core/trunk/src/java/org/apache/ivy/plu \
gins/circular/CircularDependencyHelper.java?view=diff&rev=542921&r1=542920&r2=542921 \
                ==============================================================================
                
--- incubator/ivy/core/trunk/src/java/org/apache/ivy/plugins/circular/CircularDependencyHelper.java \
                (original)
+++ incubator/ivy/core/trunk/src/java/org/apache/ivy/plugins/circular/CircularDependencyHelper.java \
Wed May 30 12:44:28 2007 @@ -17,6 +17,9 @@
  */
 package org.apache.ivy.plugins.circular;
 
+import java.util.Iterator;
+import java.util.List;
+
 import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
 import org.apache.ivy.core.module.id.ModuleRevisionId;
 
@@ -40,6 +43,20 @@
     public static String formatMessage(final ModuleDescriptor[] descriptors) {
     	return formatMessage(toMrids(descriptors));
     }
+    
+    /**
+    * @param loopElements a List<ModuleDescriptor>
+	*/
+    public static String formatMessageFromDescriptors(List loopElements) {
+    	ModuleRevisionId[] mrids = new ModuleRevisionId[loopElements.size()];
+		int pos = 0;
+		for (Iterator it = loopElements.iterator(); it.hasNext();) {
+			ModuleDescriptor descriptor = (ModuleDescriptor) it.next();
+			mrids[pos] = descriptor.getModuleRevisionId();
+			pos++;
+		}
+		return formatMessage(mrids);
+	}
 
 	public static ModuleRevisionId[] toMrids(ModuleDescriptor[] descriptors) {
 		ModuleRevisionId[] mrids = new ModuleRevisionId[descriptors.length];
@@ -48,5 +65,6 @@
 		}
 		return mrids;
 	}
+	
 
 }

Modified: incubator/ivy/core/trunk/src/java/org/apache/ivy/tools/analyser/JarJarDependencyAnalyser.java
                
URL: http://svn.apache.org/viewvc/incubator/ivy/core/trunk/src/java/org/apache/ivy/too \
ls/analyser/JarJarDependencyAnalyser.java?view=diff&rev=542921&r1=542920&r2=542921 \
                ==============================================================================
                
--- incubator/ivy/core/trunk/src/java/org/apache/ivy/tools/analyser/JarJarDependencyAnalyser.java \
                (original)
+++ incubator/ivy/core/trunk/src/java/org/apache/ivy/tools/analyser/JarJarDependencyAnalyser.java \
Wed May 30 12:44:28 2007 @@ -73,7 +73,7 @@
 
 				DefaultModuleDescriptor md = (DefaultModuleDescriptor) \
mds.get(module.getMrid());  
-				DefaultDependencyDescriptor dd = new \
DefaultDependencyDescriptor(dependency.getMrid(), false); \
+				DefaultDependencyDescriptor dd = new \
                DefaultDependencyDescriptor(md,dependency.getMrid(), false, false, \
                true);
 				dd.addDependencyConfiguration(ModuleDescriptor.DEFAULT_CONFIGURATION, \
ModuleDescriptor.DEFAULT_CONFIGURATION);  md.addDependency(dd);
 			}

Modified: incubator/ivy/core/trunk/test/java/org/apache/ivy/core/sort/SortTest.java
URL: http://svn.apache.org/viewvc/incubator/ivy/core/trunk/test/java/org/apache/ivy/core/sort/SortTest.java?view=diff&rev=542921&r1=542920&r2=542921
 ==============================================================================
--- incubator/ivy/core/trunk/test/java/org/apache/ivy/core/sort/SortTest.java \
                (original)
+++ incubator/ivy/core/trunk/test/java/org/apache/ivy/core/sort/SortTest.java Wed May \
30 12:44:28 2007 @@ -19,98 +19,314 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Date;
+import java.util.Iterator;
 import java.util.List;
 
+import junit.framework.Assert;
 import junit.framework.TestCase;
 
 import org.apache.ivy.Ivy;
 import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
 import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
 import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.plugins.circular.CircularDependencyHelper;
+import org.apache.ivy.plugins.circular.CircularDependencyStrategy;
+import org.apache.ivy.util.Message;
 
-/**
- */
 public class SortTest extends TestCase {
     
-    private ModuleRevisionId mrid1;
-    private ModuleRevisionId mrid2;
-    private ModuleRevisionId mrid3;
-    private ModuleRevisionId mrid4;
-    private DefaultModuleDescriptor[] md;
-    List toSort;
-    
-    
-    
+	private DefaultModuleDescriptor md1;
+    private DefaultModuleDescriptor md2;
+    private DefaultModuleDescriptor md3;
+    private DefaultModuleDescriptor md4;
+	private static Ivy ivy;
+	
     /* (non-Javadoc)
      * @see junit.framework.TestCase#setUp()
      */
     protected void setUp() throws Exception {
         super.setUp();
-        mrid1 = ModuleRevisionId.newInstance("org", "md1", "rev1");
-        mrid2 = ModuleRevisionId.newInstance("org", "md2", "rev2");
-        mrid3 = ModuleRevisionId.newInstance("org", "md3", "rev3");
-        mrid4 = ModuleRevisionId.newInstance("org", "md4", "rev4");
-        md = new DefaultModuleDescriptor[] {
-            new DefaultModuleDescriptor(mrid1, "integration", new Date()),
-            new DefaultModuleDescriptor(mrid2, "integration", new Date()),
-            new DefaultModuleDescriptor(mrid3, "integration", new Date()),
-            new DefaultModuleDescriptor(mrid4, "integration", new Date())
-        };
-        md[1].addDependency(new DefaultDependencyDescriptor(mrid1, false));
-        md[2].addDependency(new DefaultDependencyDescriptor(mrid2, false));
-        md[3].addDependency(new DefaultDependencyDescriptor(mrid3, false));
+        md1 = createModuleDescriptorToSort("md1", null); //The revison is often not \
set in the ivy.xml file that are ordered +        md2 = \
createModuleDescriptorToSort("md2", "rev2");//But somtimes they are set +        md3 \
= createModuleDescriptorToSort("md3", "rev3"); +        md4 = \
createModuleDescriptorToSort("md4", "rev4");  
+        ivy = new Ivy();
+		ivy.configureDefault();
+		Message.setImpl(null);
     }
 
-    public void testSort() throws Exception {
-        Ivy ivy = new Ivy();
-        ivy.configureDefault();
-        toSort = new ArrayList(Arrays.asList(new Object[] {md[0], md[2], md[1], \
                md[3]}));
-        assertSorted(md, ivy.sortModuleDescriptors(toSort));
-        toSort = new ArrayList(Arrays.asList(new Object[] {md[0], md[1], md[2], \
                md[3]}));
-        assertSorted(md, ivy.sortModuleDescriptors(toSort));
-        toSort = new ArrayList(Arrays.asList(new Object[] {md[1], md[0], md[2], \
                md[3]}));
-        assertSorted(md, ivy.sortModuleDescriptors(toSort));
-        toSort = new ArrayList(Arrays.asList(new Object[] {md[1], md[2], md[0], \
                md[3]}));
-        assertSorted(md, ivy.sortModuleDescriptors(toSort));
-        toSort = new ArrayList(Arrays.asList(new Object[] {md[2], md[1], md[0], \
                md[3]}));
-        assertSorted(md, ivy.sortModuleDescriptors(toSort));
-        toSort = new ArrayList(Arrays.asList(new Object[] {md[2], md[0], md[1], \
                md[3]}));
-        assertSorted(md, ivy.sortModuleDescriptors(toSort));
-        toSort = new ArrayList(Arrays.asList(new Object[] {md[1], md[3], md[2], \
                md[0]}));
-        assertSorted(md, ivy.sortModuleDescriptors(toSort));
-    }
-
-    private void assertSorted(DefaultModuleDescriptor[] md, List sorted) {
-        assertEquals(md.length, sorted.size());
-        for (int i = 0; i < md.length; i++) {
-            assertEquals(md[i], sorted.get(i));
-        }
-    }
     
-    // sorter does not throw circular dependency, circular dependencies are handled \
                at resolve time only
-    // because circular dependencies are more complicated to evaluate than just a \
callstack comparison +    protected void tearDown() throws Exception {
+    	Message.sumupProblems();//purge the warning and error messages
+    	Message.setImpl(null);
+    }
+
+	public void testSort() throws Exception {        
+        addDependency(md2,"md1" , "rev1");
+        addDependency(md3,"md2" , "rev2");
+        addDependency(md4,"md3" , "rev3");
+
+        DefaultModuleDescriptor[][] expectedOrder = new DefaultModuleDescriptor[][] \
{{ md1, md2, md3, md4 }}; +        
+        Collection permutations = getAllLists(md1, md3, md2, md4);
+        for (Iterator it = permutations.iterator(); it.hasNext();) {
+			List toSort = (List) it.next();
+	        assertSorted(expectedOrder, ivy.sortModuleDescriptors(toSort));			
+		}
+    }
+
     
+	
+    /**
+     * Sorter does not throw circular dependency, circular dependencies are handled \
at resolve time only. +     * However the sort respect the transitive order when it \
is unambiguous. (if A depends transitively of B, +     * but B doesn't depends \
transitively on A then B always comes before A). +     */
     public void testCircularDependency() throws Exception {
-        Ivy ivy = new Ivy();
-        ivy.configureDefault();
-        md[0].addDependency(new DefaultDependencyDescriptor(mrid4, false));
-        toSort = new ArrayList(Arrays.asList(new Object[] {md[0], md[2], md[1], \
                md[3]}));
-        // the sorted array may begin by any of the modules since there is a \
                circular dependency
-        // in this case, the result is the following
-        DefaultModuleDescriptor[] sorted = new DefaultModuleDescriptor[] {
-                md[1], md[2], md[3], md[0]
+        addDependency(md1,"md4" , "rev4");
+        addDependency(md2,"md1" , "rev1");
+        addDependency(md3,"md2" , "rev2");
+        addDependency(md4,"md3" , "rev3");
+        
+        DefaultModuleDescriptor[][] possibleOrder = new DefaultModuleDescriptor[][] \
{ +                {md2, md3, md4, md1},
+                {md3, md4, md1, md2},
+                {md4, md1, md2, md3},
+                {md1, md2, md3, md4}
             };
 
-        assertSorted(sorted, ivy.sortModuleDescriptors(toSort));
+        Collection permutations = getAllLists(md1, md3, md2, md4);
+        for (Iterator it = permutations.iterator(); it.hasNext();) {
+			List toSort = (List) it.next();
+	        assertSorted(possibleOrder, ivy.sortModuleDescriptors(toSort));
+		}        
     }
     
     public void testCircularDependency2() throws Exception {
-        Ivy ivy = new Ivy();
-        ivy.configureDefault();
-        md[1].addDependency(new DefaultDependencyDescriptor(mrid3, false));
-        toSort = new ArrayList(Arrays.asList(new Object[] {md[0], md[2], md[1], \
                md[3]}));
-        assertSorted(md, ivy.sortModuleDescriptors(toSort));
+        addDependency(md2,"md3" , "rev3");
+        addDependency(md2,"md1" , "rev1");
+        addDependency(md3,"md2" , "rev2");
+        addDependency(md4,"md3" , "rev3");
+        DefaultModuleDescriptor[][] possibleOrder = new DefaultModuleDescriptor[][] \
{ +                {md1, md3, md2, md4},
+                {md1, md2, md3, md4}//,
+                //{md3, md1, md2, md4}	//It should be better to not have this \
solution.  The loop should apear has one contigous element. +            };
+        Collection permutations = getAllLists(md1, md3, md2, md4);
+        for (Iterator it = permutations.iterator(); it.hasNext();) {
+			List toSort = (List) it.next();
+	        assertSorted(possibleOrder, ivy.sortModuleDescriptors(toSort));
+		}
     }
+
+    
+    /**
+     *  In case of Circular dependency a warning is generated.
+     */
+    public void testCircularDependencyReport() {
+        addDependency(md2,"md3" , "rev3");
+        addDependency(md2,"md1" , "rev1");
+        addDependency(md3,"md2" , "rev2");
+        addDependency(md4,"md3" , "rev3");
+
+        //MockMessageImpl mockMessageImpl = new MockMessageImpl();
+		//Message.setImpl(mockMessageImpl);
+
+		//Would be much easier with a tool like jmock 
+		class CircularDependencyReporterMock implements CircularDependencyStrategy {
+			private int nbOfCall = 0;
+			public String getName() {
+				return "CircularDependencyReporterMock";
+			}
+			public void handleCircularDependency(ModuleRevisionId[] mrids) {
+				assertEquals("handleCircularDependency is expected to be called only once" , 0, \
nbOfCall); +				assertTrue("incorrect cicular dependency invocation" + \
CircularDependencyHelper.formatMessage(mrids),  +						mrids.length == 3 && ( 
+						(mrids[0].equals(md2.getModuleRevisionId()) && \
mrids[1].equals(md3.getModuleRevisionId()) && \
mrids[2].equals(md2.getModuleRevisionId())) ||  \
+						(mrids[0].equals(md3.getModuleRevisionId()) && \
mrids[1].equals(md2.getModuleRevisionId()) && \
mrids[2].equals(md3.getModuleRevisionId())) +						));
+				nbOfCall++;				
+			}
+			public void validate() {
+				Assert.assertEquals("handleCircularDependency has nor been called" , 1, \
nbOfCall); +			}
+		}
+		CircularDependencyReporterMock circularDepReportMock = new \
CircularDependencyReporterMock(); \
+		ivy.getSettings().setCircularDependencyStrategy(circularDepReportMock); +        
+		List toSort = Arrays.asList(new ModuleDescriptor[] {md4 , md3 , md2 , md1});
+		ivy.sortModuleDescriptors(toSort);
+		
+		circularDepReportMock.validate();
+        //mockMessageImpl.assertLogWarningContains("circular dependency detected \
during sort: [ org | md3 | rev3 ]->[ org | md2 | rev2 ]->[ org | md3 | rev3 ]"); +    \
} +        
+    /**
+     * The dependency can ask for the latest integration.  It should match whatever \
the version declared in the modules to order. +     */
+    public void testLatestIntegration() {
+    	
+    	addDependency(md2,"md1" , "latest.integration");
+        addDependency(md3,"md2" , "latest.integration");
+        addDependency(md4,"md3" , "latest.integration");
+
+        DefaultModuleDescriptor[][] expectedOrder = new DefaultModuleDescriptor[][] \
{{ md1, md2, md3, md4 }}; +        
+        Collection permutations = getAllLists(md1, md3, md2, md4);
+        for (Iterator it = permutations.iterator(); it.hasNext();) {
+			List toSort = (List) it.next();
+	        assertSorted(expectedOrder, ivy.sortModuleDescriptors(toSort));			
+		}
+
+    }
+    
+    /**
+     * When the version asked by a dependency is not compatible with the version \
declared in the module to order,  +     * the two modules should be considered as \
independant +     * NB:  I'm sure of what 'compatible' means !
+     */
+    public void testDifferentVersionNotConsidered() {
+    	//To test it, I use a 'broken' loop (in one step, I change the revision) in \
such a way that I get only one solution.  If the loop was  +    	//complete more \
solutions where possible. +
+        addDependency(md1,"md4" , "rev4-other");
+        addDependency(md2,"md1" , "rev1");
+        addDependency(md3,"md2" , "rev2");
+        addDependency(md4,"md3" , "rev3");
+        
+        DefaultModuleDescriptor[][] possibleOrder = new DefaultModuleDescriptor[][] \
{ +                {md1, md2, md3, md4}
+            };
+
+        Collection permutations = getAllLists(md1, md3, md2, md4);
+        for (Iterator it = permutations.iterator(); it.hasNext();) {
+			List toSort = (List) it.next();
+	        assertSorted(possibleOrder, ivy.sortModuleDescriptors(toSort));
+		}        
+
+    }
+    
+
+    /**
+     *  In case of Different version a warning is generated.
+     */
+    public void testDifferentVersionWarning() {
+    	final DependencyDescriptor md4OtherDep = addDependency(md1,"md4" , \
"rev4-other"); +        addDependency(md2,"md1" , "rev1");
+        addDependency(md3,"md2" , "rev2");
+        addDependency(md4,"md3" , "rev3");
+
+		//Would be much easier with a tool like jmock 
+		class NonMatchingVersionReporterMock implements NonMatchingVersionReporter {
+			private int nbOfCall = 0;
+			public void reportNonMatchingVersion(DependencyDescriptor descriptor, \
ModuleDescriptor md) { +				Assert.assertEquals("reportNonMatchingVersion should be \
invokded only once" , 0, nbOfCall); +				Assert.assertEquals(md4OtherDep, \
descriptor); +				Assert.assertEquals(md4, md);
+				nbOfCall++;
+			}
+			public void validate() {
+				Assert.assertEquals("reportNonMatchingVersion has not be called" , 1, nbOfCall);
+			}
+		}
+		NonMatchingVersionReporterMock nonMatchingVersionReporterMock = new \
NonMatchingVersionReporterMock(); +		List toSort = Arrays.asList(new \
ModuleDescriptor[] {md4 , md3 , md2 , md1}); +		ivy.sortModuleDescriptors(toSort , \
nonMatchingVersionReporterMock); +		nonMatchingVersionReporterMock.validate();
+    }
+
+    
+    
+    
+    
+    
+    private DefaultModuleDescriptor createModuleDescriptorToSort(String moduleName, \
String revision) { +    	ModuleRevisionId mrid = ModuleRevisionId.newInstance("org", \
moduleName, revision); +    	return new DefaultModuleDescriptor(mrid, "integration", \
new Date()); +    }
+    
+    private DependencyDescriptor addDependency(DefaultModuleDescriptor parent, \
String moduleName, String revision) { +    	ModuleRevisionId mrid = \
ModuleRevisionId.newInstance("org", moduleName, revision); +    	DependencyDescriptor \
depDescr =  new DefaultDependencyDescriptor(parent , mrid , false, false, true); +    \
parent.addDependency(depDescr); +    	return depDescr;
+	}
+
+    /** 
+     * Verifies that sorted in one of the list of listOfPossibleSort.
+     * @param An array of possible sort result
+     * @param The actual sortedList to compare 
+     */
+    private void assertSorted(DefaultModuleDescriptor[][] listOfPossibleSort, List \
sorted) { +    	for (int i = 0; i < listOfPossibleSort.length; i++) {
+			DefaultModuleDescriptor[] expectedList = listOfPossibleSort[i];
+			assertEquals(expectedList.length, sorted.size());
+			boolean isExpected = true;
+	        for (int j = 0; j < expectedList.length; j++) {
+	            if(!expectedList[j].equals(sorted.get(j))) {
+	            	isExpected = false;
+	            	break;
+	            }
+	        }
+	        if (isExpected) {
+	        	return;
+	        }
+		}
+    	//failed, build a nice message
+        StringBuffer errorMessage = new StringBuffer();
+        errorMessage.append("Unexpected order : \n{ ");
+        for (int i = 0; i < sorted.size(); i++) {
+			if (i>0) errorMessage.append(" , ");
+			errorMessage.append(((DefaultModuleDescriptor)sorted.get(i)).getModuleRevisionId());
 +		}
+        errorMessage.append("}\nEpected : \n");
+    	for (int i = 0; i < listOfPossibleSort.length; i++) {
+			DefaultModuleDescriptor[] expectedList = listOfPossibleSort[i];
+			if (i>0) errorMessage.append(" or\n");
+			errorMessage.append("{ ");
+			for (int j = 0; j < expectedList.length; j++) {
+				if (j>0) errorMessage.append(" , ");
+				errorMessage.append(expectedList[j].getModuleRevisionId());
+			}
+			errorMessage.append(" } ");
+		}
+    	fail(errorMessage.toString());
+    }
+    
+	/** Returns a collection of lists that contains the elements a,b,c and d */
+    private Collection getAllLists(Object a, Object b, Object c, Object d) {
+		ArrayList r = new ArrayList(24);
+		r.add(Arrays.asList(new Object[] {a, b, c, d}));
+		r.add(Arrays.asList(new Object[] {a, b, d, c}));
+		r.add(Arrays.asList(new Object[] {a, c, b, d}));
+		r.add(Arrays.asList(new Object[] {a, c, d, b}));
+		r.add(Arrays.asList(new Object[] {a, d, b, c}));
+		r.add(Arrays.asList(new Object[] {a, d, c, b}));
+		r.add(Arrays.asList(new Object[] {b, a, c, d}));
+		r.add(Arrays.asList(new Object[] {b, a, d, c}));
+		r.add(Arrays.asList(new Object[] {b, c, a, d}));
+		r.add(Arrays.asList(new Object[] {b, c, d, a}));
+		r.add(Arrays.asList(new Object[] {b, d, a, c}));
+		r.add(Arrays.asList(new Object[] {b, d, c, a}));
+		r.add(Arrays.asList(new Object[] {c, b, a, d}));
+		r.add(Arrays.asList(new Object[] {c, b, d, a}));
+		r.add(Arrays.asList(new Object[] {c, a, b, d}));
+		r.add(Arrays.asList(new Object[] {c, a, d, b}));
+		r.add(Arrays.asList(new Object[] {c, d, b, a}));
+		r.add(Arrays.asList(new Object[] {c, d, a, b}));
+		r.add(Arrays.asList(new Object[] {d, b, c, a}));
+		r.add(Arrays.asList(new Object[] {d, b, a, c}));
+		r.add(Arrays.asList(new Object[] {d, c, b, a}));
+		r.add(Arrays.asList(new Object[] {d, c, a, b}));
+		r.add(Arrays.asList(new Object[] {d, a, b, c}));
+		r.add(Arrays.asList(new Object[] {d, a, c, b}));
+		return r;
+	}
+
+
 }

Modified: incubator/ivy/core/trunk/test/java/org/apache/ivy/util/MockMessageImpl.java
URL: http://svn.apache.org/viewvc/incubator/ivy/core/trunk/test/java/org/apache/ivy/util/MockMessageImpl.java?view=diff&rev=542921&r1=542920&r2=542921
 ==============================================================================
--- incubator/ivy/core/trunk/test/java/org/apache/ivy/util/MockMessageImpl.java \
                (original)
+++ incubator/ivy/core/trunk/test/java/org/apache/ivy/util/MockMessageImpl.java Wed \
May 30 12:44:28 2007 @@ -76,9 +76,24 @@
 				return;
 			}
 		}
-		throw new AssertionFailedError("logs do not contain expected message: \
expected='"+message+"' logs='"+join(_logs)+"'" ); +		throw new \
AssertionFailedError("logs do not contain expected message: expected='"+message+"' \
logs='\n"+join(_logs)+"'" );  
 	}
+
+	public void assertLogInfoContains(String message) {
+		assertLogContains(Message.MSG_INFO + " " + message);
+	}
+
+	public void assertLogWarningContains(String message) {
+		Message.sumupProblems();
+		assertLogContains(Message.MSG_WARN + " \t" + message);
+	}
+	
+	public void assertLogErrorContains(String message) {
+		Message.sumupProblems();
+		assertLogContains(Message.MSG_ERR + " " + message);
+	}
+
 
 	private String join(List logs) {
 		StringBuffer sb = new StringBuffer();


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic