IDEA-109437 Cannot set Gradle home in Cardea

1. Provide ability to setup generic external system settings listener for particular external system topic;
2. Refresh linked gradle projects automatically on gradle service directory change;
This commit is contained in:
Denis.Zhdanov
2013-06-24 20:10:25 +04:00
parent df0344bc51
commit 174aa3b2bb
7 changed files with 226 additions and 45 deletions
@@ -43,6 +43,26 @@ public abstract class AbstractExternalSystemSettings<S extends ExternalProjectSe
myChangesTopic = topic;
myProject = project;
}
@NotNull
public Project getProject() {
return myProject;
}
/**
* Every time particular external system setting is changed corresponding message is sent via ide
* <a href="http://confluence.jetbrains.com/display/IDEADEV/IntelliJ+IDEA+Messaging+infrastructure">messaging sub-system</a>.
* 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.
* <p/>
* 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<S> listener);
@SuppressWarnings("unchecked")
@NotNull
@@ -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<S extends ExternalProjectSettings> implements ExternalSystemSettingsListener<S> {
@NotNull private final ExternalSystemSettingsListener<S> myDelegate;
public DelegatingExternalSystemSettingsListener(@NotNull ExternalSystemSettingsListener<S> delegate) {
myDelegate = delegate;
}
@Override
public void onProjectsLinked(@NotNull Collection<S> settings) {
myDelegate.onProjectsLinked(settings);
}
@Override
public void onProjectsUnlinked(@NotNull Set<String> 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();
}
}
@@ -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<LibraryData, Libra
}
public void syncPaths(@NotNull final LibraryData externalLibrary, @NotNull final Library ideLibrary, boolean synchronous) {
final Set<String> toRemove = ContainerUtilRt.newHashSet();
final Set<String> 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<OrderRootType, Set<String>> toRemove = ContainerUtilRt.newHashMap();
final Map<OrderRootType, Set<String>> toAdd = ContainerUtilRt.newHashMap();
for (LibraryPathType pathType : LibraryPathType.values()) {
OrderRootType ideType = myLibraryPathTypeMapper.map(pathType);
HashSet<String> toAddPerType = ContainerUtilRt.newHashSet(externalLibrary.getPaths(pathType));
toAdd.put(ideType, toAddPerType);
HashSet<String> 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<LibraryData, Libra
public void run() {
Library.ModifiableModel model = ideLibrary.getModifiableModel();
try {
for (String path : toRemove) {
model.removeRoot(path, OrderRootType.CLASSES);
for (Map.Entry<OrderRootType, Set<String>> entry : toRemove.entrySet()) {
for (String path : entry.getValue()) {
model.removeRoot(path, entry.getKey());
}
}
for (Map.Entry<OrderRootType, Set<String>> entry : toAdd.entrySet()) {
Map<OrderRootType, Collection<File>> roots = ContainerUtilRt.newHashMap();
roots.put(entry.getKey(), ContainerUtil.map(entry.getValue(), PATH_TO_FILE));
registerPaths(roots, model, externalLibrary.getName());
}
Map<OrderRootType, Collection<File>> roots = ContainerUtilRt.newHashMap();
roots.put(OrderRootType.CLASSES, ContainerUtil.map(toAdd, PATH_TO_FILE));
registerPaths(roots, model, externalLibrary.getName());
}
finally {
model.commit();
@@ -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) {
@@ -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<String, String> 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<String, String> patchLinkedProjects(@NotNull Project project) {
GradleSettings settings = GradleSettings.getInstance(project);
Collection<GradleProjectSettings> correctedSettings = ContainerUtilRt.newArrayList();
Map<String/* old path */, String/* new path */> 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<ExternalProjectPojo, Collection<ExternalProjectPojo>> adjustedAvailableProjects = ContainerUtilRt.newHashMap();
for (Map.Entry<ExternalProjectPojo, Collection<ExternalProjectPojo>> 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<String, String> adjustedPaths, @NotNull GradleLocalSettings localSettings) {
Map<String, Collection<ExternalTaskPojo>> adjustedAvailableTasks = ContainerUtilRt.newHashMap();
for (Map.Entry<String, Collection<ExternalTaskPojo>> 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<String, String> adjustedPaths, @NotNull GradleLocalSettings localSettings) {
Map<ExternalProjectPojo, Collection<ExternalProjectPojo>> adjustedAvailableProjects = ContainerUtilRt.newHashMap();
for (Map.Entry<ExternalProjectPojo, Collection<ExternalProjectPojo>> 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<String, String> 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);
}
}
}
}
@@ -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<GradleProjectSettings>
implements GradleSettingsListener
{
public DelegatingGradleSettingsListenerAdapter(@NotNull ExternalSystemSettingsListener<GradleProjectSettings> 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) {
}
}
@@ -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<GradleProject
return ServiceManager.getService(project, GradleSettings.class);
}
@Override
public void subscribe(@NotNull ExternalSystemSettingsListener<GradleProjectSettings> listener) {
getProject().getMessageBus().connect(getProject()).subscribe(GradleSettingsListener.TOPIC,
new DelegatingGradleSettingsListenerAdapter(listener));
}
@SuppressWarnings("unchecked")
@Nullable
@Override
@@ -80,10 +89,11 @@ public class GradleSettings extends AbstractExternalSystemSettings<GradleProject
return myServiceDirectoryPath;
}
public void setServiceDirectoryPath(@Nullable String path) {
if (!Comparing.equal(myServiceDirectoryPath, path)) {
final String oldPath = myServiceDirectoryPath;
getPublisher().onServiceDirectoryPathChange(oldPath, path);
public void setServiceDirectoryPath(@Nullable String newPath) {
if (!Comparing.equal(myServiceDirectoryPath, newPath)) {
String oldPath = myServiceDirectoryPath;
myServiceDirectoryPath = newPath;
getPublisher().onServiceDirectoryPathChange(oldPath, newPath);
}
}