diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/settings/AbstractExternalSystemSettings.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/settings/AbstractExternalSystemSettings.java
index d947a51f2abd..253cb6d0ceea 100644
--- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/settings/AbstractExternalSystemSettings.java
+++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/settings/AbstractExternalSystemSettings.java
@@ -43,6 +43,26 @@ public abstract class AbstractExternalSystemSettingsmessaging sub-system.
+ * The problem is that every external system implementation defines it's own topic/listener pair. Listener interface is derived
+ * from the common {@link ExternalSystemSettingsListener} interface and is specific to external sub-system implementation.
+ * However, it's possible that a client wants to perform particular actions based only on {@link ExternalSystemSettingsListener}
+ * facilities. There is no way for such external system-agnostic client to create external system-specific listener
+ * implementation then.
+ *
+ * That's why this method allows to wrap given 'generic listener' into external system-specific one.
+ *
+ * @param listener target generic listener to wrap to external system-specific implementation
+ */
+ public abstract void subscribe(@NotNull ExternalSystemSettingsListener listener);
@SuppressWarnings("unchecked")
@NotNull
diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/settings/DelegatingExternalSystemSettingsListener.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/settings/DelegatingExternalSystemSettingsListener.java
new file mode 100644
index 000000000000..6afb85028bb9
--- /dev/null
+++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/settings/DelegatingExternalSystemSettingsListener.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2000-2013 JetBrains s.r.o.
+ *
+ * Licensed 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 com.intellij.openapi.externalSystem.settings;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * @author Denis Zhdanov
+ * @since 6/24/13 6:23 PM
+ */
+public class DelegatingExternalSystemSettingsListener implements ExternalSystemSettingsListener {
+
+ @NotNull private final ExternalSystemSettingsListener myDelegate;
+
+ public DelegatingExternalSystemSettingsListener(@NotNull ExternalSystemSettingsListener delegate) {
+ myDelegate = delegate;
+ }
+
+ @Override
+ public void onProjectsLinked(@NotNull Collection settings) {
+ myDelegate.onProjectsLinked(settings);
+ }
+
+ @Override
+ public void onProjectsUnlinked(@NotNull Set linkedProjectPaths) {
+ myDelegate.onProjectsUnlinked(linkedProjectPaths);
+ }
+
+ @Override
+ public void onUseAutoImportChange(boolean currentValue, @NotNull String linkedProjectPath) {
+ myDelegate.onUseAutoImportChange(currentValue, linkedProjectPath);
+ }
+
+ @Override
+ public void onBulkChangeStart() {
+ myDelegate.onBulkChangeStart();
+ }
+
+ @Override
+ public void onBulkChangeEnd() {
+ myDelegate.onBulkChangeEnd();
+ }
+}
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/LibraryDataService.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/LibraryDataService.java
index 48777a134daa..5d7bbe1d8f5a 100644
--- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/LibraryDataService.java
+++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/LibraryDataService.java
@@ -27,6 +27,7 @@ import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.util.Collection;
+import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@@ -181,12 +182,21 @@ public class LibraryDataService implements ProjectDataService toRemove = ContainerUtilRt.newHashSet();
- final Set toAdd = ContainerUtilRt.newHashSet(externalLibrary.getPaths(LibraryPathType.BINARY));
- for (VirtualFile ideFile : ideLibrary.getFiles(OrderRootType.CLASSES)) {
- String idePath = ExternalSystemApiUtil.getLocalFileSystemPath(ideFile);
- if (!toAdd.remove(idePath)) {
- toRemove.add(idePath);
+ final Map> toRemove = ContainerUtilRt.newHashMap();
+ final Map> toAdd = ContainerUtilRt.newHashMap();
+ for (LibraryPathType pathType : LibraryPathType.values()) {
+ OrderRootType ideType = myLibraryPathTypeMapper.map(pathType);
+ HashSet toAddPerType = ContainerUtilRt.newHashSet(externalLibrary.getPaths(pathType));
+ toAdd.put(ideType, toAddPerType);
+
+ HashSet toRemovePerType = ContainerUtilRt.newHashSet();
+ toRemove.put(ideType, toRemovePerType);
+
+ for (VirtualFile ideFile : ideLibrary.getFiles(ideType)) {
+ String idePath = ExternalSystemApiUtil.getLocalFileSystemPath(ideFile);
+ if (!toAddPerType.remove(idePath)) {
+ toRemovePerType.add(ideFile.getUrl());
+ }
}
}
if (toRemove.isEmpty() && toAdd.isEmpty()) {
@@ -197,12 +207,17 @@ public class LibraryDataService implements ProjectDataService> entry : toRemove.entrySet()) {
+ for (String path : entry.getValue()) {
+ model.removeRoot(path, entry.getKey());
+ }
+ }
+
+ for (Map.Entry> entry : toAdd.entrySet()) {
+ Map> roots = ContainerUtilRt.newHashMap();
+ roots.put(entry.getKey(), ContainerUtil.map(entry.getValue(), PATH_TO_FILE));
+ registerPaths(roots, model, externalLibrary.getName());
}
- Map> roots = ContainerUtilRt.newHashMap();
- roots.put(OrderRootType.CLASSES, ContainerUtil.map(toAdd, PATH_TO_FILE));
- registerPaths(roots, model, externalLibrary.getName());
}
finally {
model.commit();
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ui/ExternalToolWindowManager.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ui/ExternalToolWindowManager.java
index 5ccb96799a38..2abba6d1a266 100644
--- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ui/ExternalToolWindowManager.java
+++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ui/ExternalToolWindowManager.java
@@ -26,10 +26,11 @@ import java.util.Set;
*/
public class ExternalToolWindowManager {
+ @SuppressWarnings("unchecked")
public static void handle(@NotNull final Project project) {
for (final ExternalSystemManager, ?, ?, ?, ?> manager : ExternalSystemApiUtil.getAllManagers()) {
final AbstractExternalSystemSettings settings = manager.getSettingsProvider().fun(project);
- project.getMessageBus().connect(project).subscribe(settings.getChangesTopic(), new ExternalSystemSettingsListenerAdapter() {
+ settings.subscribe(new ExternalSystemSettingsListenerAdapter() {
@Override
public void onProjectsLinked(@NotNull Collection linked) {
if (settings.getLinkedProjectsSettings().size() != 1) {
diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/GradleManager.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/GradleManager.java
index 42dbef19bbad..c320c17dbeda 100644
--- a/plugins/gradle/src/org/jetbrains/plugins/gradle/GradleManager.java
+++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/GradleManager.java
@@ -34,6 +34,7 @@ import com.intellij.openapi.externalSystem.service.project.autoimport.CachingExt
import com.intellij.openapi.externalSystem.service.ui.DefaultExternalSystemUiAware;
import com.intellij.openapi.externalSystem.task.ExternalSystemTaskManager;
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
+import com.intellij.openapi.externalSystem.util.ExternalSystemUtil;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.module.EmptyModuleType;
import com.intellij.openapi.module.JavaModuleType;
@@ -50,10 +51,12 @@ import com.intellij.util.Function;
import com.intellij.util.PathUtil;
import com.intellij.util.PathsList;
import com.intellij.util.containers.ContainerUtilRt;
+import com.intellij.util.messages.MessageBusConnection;
import icons.GradleIcons;
import org.gradle.tooling.ProjectConnection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import org.jetbrains.plugins.gradle.config.GradleSettingsListenerAdapter;
import org.jetbrains.plugins.gradle.remote.GradleJavaHelper;
import org.jetbrains.plugins.gradle.remote.impl.GradleTaskManager;
import org.jetbrains.plugins.gradle.service.GradleInstallationManager;
@@ -279,11 +282,33 @@ implements ExternalSystemConfigurableAware, ExternalSystemUiAware, ExternalSyste
}
@Override
- public void runActivity(Project project) {
+ public void runActivity(final Project project) {
+ // We want to automatically refresh linked projects on gradle service directory change.
+ MessageBusConnection connection = project.getMessageBus().connect(project);
+ connection.subscribe(GradleSettings.getInstance(project).getChangesTopic(), new GradleSettingsListenerAdapter() {
+ @Override
+ public void onServiceDirectoryPathChange(@Nullable String oldPath, @Nullable String newPath) {
+ ExternalSystemUtil.refreshProjects(project, GradleConstants.SYSTEM_ID, true);
+ }
+ });
+
// We used to assume that gradle scripts are always named 'build.gradle' and kept path to that build.gradle file at ide settings.
- // However, it was found out that that is incorrect assumption (IDEA-109064). Now we keep paths to gradle script's directories
+ // However, it was found out that that is incorrect assumption (IDEA-109064). Now we keep paths to gradle script's directories
// instead. However, we don't want to force old users to re-import gradle projects because of that. That's why we check gradle
// config and re-point it from build.gradle to the parent dir if necessary.
+ Map adjustedPaths = patchLinkedProjects(project);
+ if (adjustedPaths == null) {
+ return;
+ }
+
+ GradleLocalSettings localSettings = GradleLocalSettings.getInstance(project);
+ patchRecentTasks(adjustedPaths, localSettings);
+ patchAvailableProjects(adjustedPaths, localSettings);
+ patchAvailableTasks(adjustedPaths, localSettings);
+ }
+
+ @Nullable
+ private static Map patchLinkedProjects(@NotNull Project project) {
GradleSettings settings = GradleSettings.getInstance(project);
Collection correctedSettings = ContainerUtilRt.newArrayList();
Map adjustedPaths = ContainerUtilRt.newHashMap();
@@ -305,35 +330,14 @@ implements ExternalSystemConfigurableAware, ExternalSystemUiAware, ExternalSyste
correctedSettings.add(projectSettings);
}
if (adjustedPaths.isEmpty()) {
- return;
+ return null;
}
-
- settings.setLinkedProjectsSettings(correctedSettings);
- GradleLocalSettings localSettings = GradleLocalSettings.getInstance(project);
- // Recent tasks.
- for (ExternalTaskExecutionInfo taskInfo : localSettings.getRecentTasks()) {
- ExternalSystemTaskExecutionSettings s = taskInfo.getSettings();
- String newPath = adjustedPaths.get(s.getExternalProjectPath());
- if (newPath != null) {
- s.setExternalProjectPath(newPath);
- }
- }
-
- // Available projects.
- Map> adjustedAvailableProjects = ContainerUtilRt.newHashMap();
- for (Map.Entry> entry : localSettings.getAvailableProjects().entrySet()) {
- String newPath = adjustedPaths.get(entry.getKey().getPath());
- if (newPath == null) {
- adjustedAvailableProjects.put(entry.getKey(), entry.getValue());
- }
- else {
- adjustedAvailableProjects.put(new ExternalProjectPojo(entry.getKey().getName(), newPath), entry.getValue());
- }
- }
- localSettings.setAvailableProjects(adjustedAvailableProjects);
-
- // Available tasks.
+ settings.setLinkedProjectsSettings(correctedSettings);
+ return adjustedPaths;
+ }
+
+ private static void patchAvailableTasks(@NotNull Map adjustedPaths, @NotNull GradleLocalSettings localSettings) {
Map> adjustedAvailableTasks = ContainerUtilRt.newHashMap();
for (Map.Entry> entry : localSettings.getAvailableTasks().entrySet()) {
String newPath = adjustedPaths.get(entry.getKey());
@@ -352,4 +356,28 @@ implements ExternalSystemConfigurableAware, ExternalSystemUiAware, ExternalSyste
}
localSettings.setAvailableTasks(adjustedAvailableTasks);
}
+
+ private static void patchAvailableProjects(@NotNull Map adjustedPaths, @NotNull GradleLocalSettings localSettings) {
+ Map> adjustedAvailableProjects = ContainerUtilRt.newHashMap();
+ for (Map.Entry> entry : localSettings.getAvailableProjects().entrySet()) {
+ String newPath = adjustedPaths.get(entry.getKey().getPath());
+ if (newPath == null) {
+ adjustedAvailableProjects.put(entry.getKey(), entry.getValue());
+ }
+ else {
+ adjustedAvailableProjects.put(new ExternalProjectPojo(entry.getKey().getName(), newPath), entry.getValue());
+ }
+ }
+ localSettings.setAvailableProjects(adjustedAvailableProjects);
+ }
+
+ private static void patchRecentTasks(@NotNull Map adjustedPaths, @NotNull GradleLocalSettings localSettings) {
+ for (ExternalTaskExecutionInfo taskInfo : localSettings.getRecentTasks()) {
+ ExternalSystemTaskExecutionSettings s = taskInfo.getSettings();
+ String newPath = adjustedPaths.get(s.getExternalProjectPath());
+ if (newPath != null) {
+ s.setExternalProjectPath(newPath);
+ }
+ }
+ }
}
diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/config/DelegatingGradleSettingsListenerAdapter.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/config/DelegatingGradleSettingsListenerAdapter.java
new file mode 100644
index 000000000000..5268fc178142
--- /dev/null
+++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/config/DelegatingGradleSettingsListenerAdapter.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2000-2013 JetBrains s.r.o.
+ *
+ * Licensed 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.jetbrains.plugins.gradle.config;
+
+import com.intellij.openapi.externalSystem.settings.DelegatingExternalSystemSettingsListener;
+import com.intellij.openapi.externalSystem.settings.ExternalSystemSettingsListener;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.plugins.gradle.settings.GradleProjectSettings;
+import org.jetbrains.plugins.gradle.settings.GradleSettingsListener;
+
+/**
+ * @author Denis Zhdanov
+ * @since 6/24/13 6:35 PM
+ */
+public class DelegatingGradleSettingsListenerAdapter extends DelegatingExternalSystemSettingsListener
+ implements GradleSettingsListener
+{
+
+ public DelegatingGradleSettingsListenerAdapter(@NotNull ExternalSystemSettingsListener delegate) {
+ super(delegate);
+ }
+
+ @Override
+ public void onGradleHomeChange(@Nullable String oldPath, @Nullable String newPath, @NotNull String linkedProjectPath) {
+ }
+
+ @Override
+ public void onPreferLocalGradleDistributionToWrapperChange(boolean currentValue, @NotNull String linkedProjectPath) {
+ }
+
+ @Override
+ public void onServiceDirectoryPathChange(@Nullable String oldPath, @Nullable String newPath) {
+ }
+}
diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/settings/GradleSettings.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/settings/GradleSettings.java
index aff64ca3845b..d9bdc07d4fcf 100644
--- a/plugins/gradle/src/org/jetbrains/plugins/gradle/settings/GradleSettings.java
+++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/settings/GradleSettings.java
@@ -17,12 +17,14 @@ package org.jetbrains.plugins.gradle.settings;
import com.intellij.openapi.components.*;
import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemSettings;
+import com.intellij.openapi.externalSystem.settings.ExternalSystemSettingsListener;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.util.containers.ContainerUtilRt;
import com.intellij.util.xmlb.annotations.AbstractCollection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import org.jetbrains.plugins.gradle.config.DelegatingGradleSettingsListenerAdapter;
import java.util.Set;
@@ -53,6 +55,13 @@ public class GradleSettings extends AbstractExternalSystemSettings listener) {
+ getProject().getMessageBus().connect(getProject()).subscribe(GradleSettingsListener.TOPIC,
+ new DelegatingGradleSettingsListenerAdapter(listener));
+
+ }
+
@SuppressWarnings("unchecked")
@Nullable
@Override
@@ -80,10 +89,11 @@ public class GradleSettings extends AbstractExternalSystemSettings