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