diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/importing/model/AbstractGradleDependency.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/importing/model/AbstractGradleDependency.java
index 1a4538dc187f..5e26f0accf18 100644
--- a/plugins/gradle/src/org/jetbrains/plugins/gradle/importing/model/AbstractGradleDependency.java
+++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/importing/model/AbstractGradleDependency.java
@@ -35,22 +35,12 @@ public abstract class AbstractGradleDependency extends AbstractGradleEntity impl
@Override
public int hashCode() {
- int result = myScope.hashCode();
- result = 31 * result + (myExported ? 1 : 0);
- return result;
+ return 31;
}
@Override
public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- AbstractGradleDependency that = (AbstractGradleDependency)o;
-
- if (myExported != that.myExported) return false;
- if (myScope != that.myScope) return false;
-
- return true;
+ return this == o || (o != null && getClass() == o.getClass());
}
@Override
diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/remote/impl/GradleLibraryNamesMixer.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/remote/impl/GradleLibraryNamesMixer.java
new file mode 100644
index 000000000000..45413cdf04e1
--- /dev/null
+++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/remote/impl/GradleLibraryNamesMixer.java
@@ -0,0 +1,222 @@
+package org.jetbrains.plugins.gradle.remote.impl;
+
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.containers.HashMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.plugins.gradle.importing.model.GradleLibrary;
+import org.jetbrains.plugins.gradle.importing.model.LibraryPathType;
+
+import java.io.File;
+import java.util.*;
+
+/**
+ * Encapsulates logic of checking if particular collection of gradle libraries contains libraries with the same names and
+ * tries to diversify them in the case of the positive answer.
+ *
+ * Thread-safe.
+ *
+ * @author Denis Zhdanov
+ * @since 10/19/11 2:04 PM
+ */
+public class GradleLibraryNamesMixer {
+
+ /**
+ * Holds mappings like ('file name'; boolean) where 'file name' defines 'too common' file/dir
+ * name that should not be used during library name generation. Boolean flag indicates if 'common file name' may be used
+ * if 'non-common' files are the same.
+ *
+ * Example: consider the following file system tree:
+ *
+ * module
+ * |_src
+ * |_main
+ * | |_resources
+ * |
+ * |_test
+ * |_resources
+ *
+ * Let's say we have two libraries where one of them points to 'src/main/resources' and another one
+ * to 'src/test/resources'. We want to generate names 'module-resources' and
+ * 'module-test-resources' respectively because 'test' entry at the current collection is
+ * stored with 'true' flag.
+ */
+ private static final Map NON_UNIQUE_PATH_ENTRIES = new HashMap();
+ static {
+ NON_UNIQUE_PATH_ENTRIES.put("src", false);
+ NON_UNIQUE_PATH_ENTRIES.put("main", false);
+ NON_UNIQUE_PATH_ENTRIES.put("test", true);
+ NON_UNIQUE_PATH_ENTRIES.put("resources", false);
+ NON_UNIQUE_PATH_ENTRIES.put("java", false);
+ NON_UNIQUE_PATH_ENTRIES.put("groovy", false);
+ }
+ private static final char NAME_SEPARATOR = '-';
+
+ /**
+ * Tries to ensure that given libraries have distinct names, i.e. traverses all of them and tries to generate
+ * unique name for those with equal names.
+ *
+ * @param libraries libraries to process
+ */
+ @SuppressWarnings("MethodMayBeStatic")
+ public void mixNames(@NotNull Iterable extends GradleLibrary> libraries) {
+ Map names = new HashMap();
+ List data = new ArrayList();
+ for (GradleLibrary library : libraries) {
+ Wrapped wrapped = new Wrapped(library);
+ data.add(wrapped);
+ }
+ boolean mixed = false;
+ while (!mixed) {
+ mixed = doMixNames(data, names);
+ }
+ }
+
+ /**
+ * Does the same as {@link #mixNames(Iterable)} but uses given ('library name; wrapped library'} mappings cache.
+ *
+ * @param libraries libraries to process
+ * @param cache cache to use
+ * @return true if all of the given libraries have distinct names now; false otherwise
+ */
+ private static boolean doMixNames(@NotNull Collection libraries, @NotNull Map cache) {
+ cache.clear();
+ for (Wrapped current : libraries) {
+ Wrapped previous = cache.remove(current.library.getName());
+ if (previous == null) {
+ cache.put(current.library.getName(), current);
+ }
+ else {
+ mixNames(current, previous);
+ return current.library.getName().equals(previous.library.getName()); // Stop processing if it's not possible to generate
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Tries to generate distinct names for the given wrapped libraries (assuming that they have equal names at the moment).
+ *
+ * @param wrapped1 one of the libraries with equal names
+ * @param wrapped2 another library which name is equal to the name of the given one
+ */
+ @SuppressWarnings("AssignmentToForLoopParameter")
+ private static void mixNames(@NotNull Wrapped wrapped1, @NotNull Wrapped wrapped2) {
+ if (!wrapped1.prepare() || !wrapped2.prepare()) {
+ return;
+ }
+ String wrapped1AltText = null;
+ String wrapped2AltText = null;
+
+ for (File file1 = wrapped1.currentFile, file2 = wrapped2.currentFile;
+ file1 != null && file2 != null;
+ file1 = file1.getParentFile(), file2 = file2.getParentFile())
+ {
+ while (file1 != null && !StringUtil.isEmpty(file1.getName()) && NON_UNIQUE_PATH_ENTRIES.containsKey(file1.getName())) {
+ if (NON_UNIQUE_PATH_ENTRIES.get(file1.getName())) {
+ if (StringUtil.isEmpty(wrapped1AltText)) {
+ wrapped1AltText = file1.getName();
+ }
+ else {
+ wrapped1AltText += NAME_SEPARATOR + file1.getName();
+ }
+ }
+ file1 = file1.getParentFile();
+ }
+ while (file2 != null && !StringUtil.isEmpty(file2.getName()) && NON_UNIQUE_PATH_ENTRIES.containsKey(file2.getName())) {
+ if (NON_UNIQUE_PATH_ENTRIES.get(file2.getName())) {
+ if (StringUtil.isEmpty(wrapped2AltText)) {
+ wrapped2AltText = file2.getName();
+ }
+ else {
+ wrapped2AltText += NAME_SEPARATOR + file2.getName();
+ }
+ }
+ file2 = file2.getParentFile();
+ }
+
+ if (file1 == null) {
+ wrapped1.nextFile();
+ }
+ if (file2 == null) {
+ wrapped2.nextFile();
+ }
+
+ if (file1 != null && file2 != null && !file1.getName().equals(file2.getName())) {
+ wrapped1.library.setName(file1.getName() + NAME_SEPARATOR + wrapped1.library.getName());
+ wrapped2.library.setName(file2.getName() + NAME_SEPARATOR + wrapped2.library.getName());
+ }
+
+ if (wrapped1.library.getName().equals(wrapped2.library.getName())) {
+ if (wrapped1AltText != null) {
+ diversifyName(wrapped1AltText, wrapped1, file1);
+ return;
+ }
+ else if (wrapped2AltText != null) {
+ diversifyName(wrapped2AltText, wrapped2, file1);
+ return;
+ }
+ }
+ else {
+ return;
+ }
+
+ if (file1 == null || file2 == null) {
+ return;
+ }
+ }
+ }
+
+ @SuppressWarnings("ConstantConditions")
+ private static void diversifyName(@NotNull String changeText, @NotNull Wrapped wrapped, @Nullable File file) {
+ String name = wrapped.library.getName();
+ int i = file == null ? - 1 : name.indexOf(file.getName());
+ final String newName;
+ if (i >= 0) {
+ newName = name.substring(0, i + file.getName().length()) + NAME_SEPARATOR + changeText + name.substring(i + file.getName().length());
+ }
+ else {
+ newName = changeText + NAME_SEPARATOR + name;
+ }
+ wrapped.library.setName(newName);
+ }
+
+ /**
+ * Wraps target library and hold auxiliary information required for the processing.
+ */
+ private static class Wrapped {
+ /** Holds list of files that may be used for name generation. */
+ public final Set files = new HashSet();
+ /** File that was used for the current name generation. */
+ public File currentFile;
+ /** Target library. */
+ public GradleLibrary library;
+
+ Wrapped(@NotNull GradleLibrary library) {
+ this.library = library;
+ for (LibraryPathType pathType : LibraryPathType.values()) {
+ String path = library.getPath(pathType);
+ if (path != null) {
+ files.add(new File(path));
+ }
+ }
+ }
+
+ public boolean prepare() {
+ if (currentFile != null) {
+ return true;
+ }
+ return nextFile();
+ }
+
+ public boolean nextFile() {
+ if (files.isEmpty()) {
+ return false;
+ }
+ Iterator iterator = files.iterator();
+ currentFile = iterator.next();
+ iterator.remove();
+ return true;
+ }
+ }
+}
diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/remote/impl/GradleLibraryNamesMixerTest.java b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/remote/impl/GradleLibraryNamesMixerTest.java
new file mode 100644
index 000000000000..dc5dc5077934
--- /dev/null
+++ b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/remote/impl/GradleLibraryNamesMixerTest.java
@@ -0,0 +1,71 @@
+package org.jetbrains.plugins.gradle.remote.impl;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.plugins.gradle.importing.model.GradleLibrary;
+import org.jetbrains.plugins.gradle.importing.model.LibraryPathType;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Denis Zhdanov
+ * @since 10/19/11 5:25 PM
+ */
+public class GradleLibraryNamesMixerTest {
+
+ private GradleLibraryNamesMixer myMixer;
+
+ @Before
+ public void setUp() {
+ myMixer = new GradleLibraryNamesMixer();
+ }
+
+ @Test
+ public void sourceVsTest() {
+ doTest(
+ t("resources", "my-module-resources", "dir1/dir2/my-module/src/main/resources"),
+ t("resources", "my-module-test-resources", "dir1/dir2/my-module/src/test/resources"),
+ t("resources", "my-another-module-resources", "dir1/dir2/my-another-module/src/main/resources"),
+ t("resources", "my-another-module-test-resources", "dir1/dir2/my-another-module/src/test/resources")
+ );
+ }
+
+ private void doTest(TestDataEntry... entries) {
+ Map expected = new IdentityHashMap();
+ List libraries = new ArrayList();
+ for (TestDataEntry entry : entries) {
+ GradleLibrary library = new GradleLibrary(entry.initialName);
+ library.addPath(LibraryPathType.BINARY, entry.path);
+ libraries.add(library);
+ expected.put(library, entry.expectedName);
+ }
+
+ myMixer.mixNames(libraries);
+ for (GradleLibrary library : libraries) {
+ assertEquals(expected.get(library), library.getName());
+ }
+ }
+
+ private static class TestDataEntry {
+
+ public String initialName;
+ public String expectedName;
+ public String path;
+
+ TestDataEntry(@NotNull String initialName, @NotNull String expectedName, @NotNull String path) {
+ this.initialName = initialName;
+ this.expectedName = expectedName;
+ this.path = path;
+ }
+ }
+
+ public static TestDataEntry t(@NotNull String initialName, @NotNull String expectedName, @NotNull String path) {
+ return new TestDataEntry(initialName, expectedName, path);
+ }
+}