incubator-ivy-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From gscok...@apache.org
Subject svn commit: r542921 - in /incubator/ivy/core/trunk: ./ doc/doc/configuration/ doc/doc/use/ src/java/org/apache/ivy/ src/java/org/apache/ivy/ant/ src/java/org/apache/ivy/core/sort/ src/java/org/apache/ivy/plugins/circular/ src/java/org/apache/ivy/tools/...
Date Wed, 30 May 2007 19:44:29 GMT
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/plugins/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/tools/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();



Mime
View raw message