blob: 7b06640f62340d2366d61e93fc76dc380eb7e13d [file] [log] [blame]
/*
* Copyright 2000-2014 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.codeInsight;
import com.intellij.ProjectTopics;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.externalSystem.model.DataNode;
import com.intellij.openapi.externalSystem.model.project.ProjectData;
import com.intellij.openapi.externalSystem.service.project.ExternalProjectRefreshCallback;
import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataManager;
import com.intellij.openapi.externalSystem.util.DisposeAwareProjectChange;
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
import com.intellij.openapi.externalSystem.util.ExternalSystemUtil;
import com.intellij.openapi.externalSystem.service.execution.ProgressExecutionMode;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.IndexNotReadyException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleRootAdapter;
import com.intellij.openapi.roots.ModuleRootEvent;
import com.intellij.openapi.roots.ex.ProjectRootManagerEx;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.EditorNotificationPanel;
import com.intellij.ui.EditorNotifications;
import org.gradle.util.GUtil;
import org.gradle.wrapper.WrapperConfiguration;
import org.gradle.wrapper.WrapperExecutor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.gradle.settings.DistributionType;
import org.jetbrains.plugins.gradle.settings.GradleProjectSettings;
import org.jetbrains.plugins.gradle.settings.GradleSettings;
import org.jetbrains.plugins.gradle.util.GradleBundle;
import org.jetbrains.plugins.gradle.util.GradleConstants;
import org.jetbrains.plugins.gradle.util.GradleUtil;
import java.io.File;
import java.net.URI;
import java.util.Collections;
import java.util.Properties;
import java.util.regex.Pattern;
/**
* @author Vladislav.Soroka
* @since 9/13/13
*/
public class UseDistributionWithSourcesNotificationProvider extends EditorNotifications.Provider<EditorNotificationPanel> {
public static final Pattern GRADLE_SRC_DISTRIBUTION_PATTERN;
private static final Logger LOG = Logger.getInstance("#" + UseDistributionWithSourcesNotificationProvider.class.getName());
private static final Key<EditorNotificationPanel> KEY = Key.create("gradle.notifications.use.distribution.with.sources");
private static final String ALL_ZIP_DISTRIBUTION_URI_SUFFIX = "-all.zip";
private final Project myProject;
static {
GRADLE_SRC_DISTRIBUTION_PATTERN = Pattern.compile("https?\\\\?://services\\.gradle\\.org.*" + ALL_ZIP_DISTRIBUTION_URI_SUFFIX);
}
public UseDistributionWithSourcesNotificationProvider(Project project, final EditorNotifications notifications) {
myProject = project;
project.getMessageBus().connect(project).subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootAdapter() {
@Override
public void rootsChanged(ModuleRootEvent event) {
notifications.updateAllNotifications();
}
});
}
@NotNull
@Override
public Key<EditorNotificationPanel> getKey() {
return KEY;
}
@Override
public EditorNotificationPanel createNotificationPanel(@NotNull VirtualFile file, @NotNull FileEditor fileEditor) {
try {
if (GradleConstants.DEFAULT_SCRIPT_NAME.equals(file.getName()) ||
GradleConstants.SETTINGS_FILE_NAME.equals(file.getName())) {
final Module module = ModuleUtilCore.findModuleForFile(file, myProject);
if (module == null) return null;
final String rootProjectPath = getRootProjectPath(module);
if (rootProjectPath == null) return null;
final GradleProjectSettings settings = GradleSettings.getInstance(module.getProject()).getLinkedProjectSettings(rootProjectPath);
if (settings == null || settings.getDistributionType() != DistributionType.DEFAULT_WRAPPED) return null;
if (settings.isDisableWrapperSourceDistributionNotification()) return null;
if (isWrapperDistributionWithSourcesUsed(rootProjectPath)) return null;
final EditorNotificationPanel panel = new EditorNotificationPanel();
panel.setText(GradleBundle.message("gradle.notifications.use.distribution.with.sources"));
panel.createActionLabel(GradleBundle.message("gradle.notifications.hide.tip"), new Runnable() {
@Override
public void run() {
settings.setDisableWrapperSourceDistributionNotification(true);
EditorNotifications.getInstance(module.getProject()).updateAllNotifications();
}
});
panel.createActionLabel(GradleBundle.message("gradle.notifications.apply.suggestion"), new Runnable() {
@Override
public void run() {
updateDefaultWrapperConfiguration(rootProjectPath);
EditorNotifications.getInstance(module.getProject()).updateAllNotifications();
final ProjectDataManager projectDataManager = ServiceManager.getService(ProjectDataManager.class);
ExternalSystemUtil.refreshProject(
module.getProject(), GradleConstants.SYSTEM_ID, settings.getExternalProjectPath(),
new ExternalProjectRefreshCallback() {
@Override
public void onSuccess(@Nullable final DataNode<ProjectData> externalProject) {
if (externalProject == null) {
return;
}
ExternalSystemApiUtil.executeProjectChangeAction(true, new DisposeAwareProjectChange(module.getProject()) {
@Override
public void execute() {
ProjectRootManagerEx.getInstanceEx(module.getProject()).mergeRootsChangesDuring(new Runnable() {
@Override
public void run() {
projectDataManager.importData(externalProject.getKey(), Collections.singleton(externalProject), module.getProject(), true);
}
});
}
});
}
@Override
public void onFailure(@NotNull String errorMessage, @Nullable String errorDetails) {
}
}, true, ProgressExecutionMode.START_IN_FOREGROUND_ASYNC);
}
});
return panel;
}
}
catch (ProcessCanceledException ignored) {
}
catch (IndexNotReadyException ignored) {
}
return null;
}
private static void updateDefaultWrapperConfiguration(@NotNull String linkedProjectPath) {
try {
final File wrapperPropertiesFile = GradleUtil.findDefaultWrapperPropertiesFile(linkedProjectPath);
if (wrapperPropertiesFile == null) return;
final WrapperConfiguration wrapperConfiguration = GradleUtil.getWrapperConfiguration(linkedProjectPath);
if (wrapperConfiguration == null) return;
String currentDistributionUri = wrapperConfiguration.getDistribution().toString();
if (StringUtil.endsWith(currentDistributionUri, ALL_ZIP_DISTRIBUTION_URI_SUFFIX)) return;
final String distributionUri =
currentDistributionUri.substring(0, currentDistributionUri.lastIndexOf('-')) + ALL_ZIP_DISTRIBUTION_URI_SUFFIX;
wrapperConfiguration.setDistribution(new URI(distributionUri));
Properties wrapperProperties = new Properties();
wrapperProperties.setProperty(WrapperExecutor.DISTRIBUTION_URL_PROPERTY, wrapperConfiguration.getDistribution().toString());
wrapperProperties.setProperty(WrapperExecutor.DISTRIBUTION_BASE_PROPERTY, wrapperConfiguration.getDistributionBase());
wrapperProperties.setProperty(WrapperExecutor.DISTRIBUTION_PATH_PROPERTY, wrapperConfiguration.getDistributionPath());
wrapperProperties.setProperty(WrapperExecutor.ZIP_STORE_BASE_PROPERTY, wrapperConfiguration.getZipBase());
wrapperProperties.setProperty(WrapperExecutor.ZIP_STORE_PATH_PROPERTY, wrapperConfiguration.getZipPath());
GUtil.saveProperties(wrapperProperties, new File(wrapperPropertiesFile.getPath()));
LocalFileSystem.getInstance().refreshIoFiles(Collections.singletonList(wrapperPropertiesFile));
}
catch (Exception e) {
LOG.error(e);
}
}
private static boolean isWrapperDistributionWithSourcesUsed(String linkedProjectPath) {
WrapperConfiguration wrapperConfiguration = GradleUtil.getWrapperConfiguration(linkedProjectPath);
// currently only wrapped distribution takes into account
if (wrapperConfiguration == null) return true;
String distributionUri = wrapperConfiguration.getDistribution().toString();
return GRADLE_SRC_DISTRIBUTION_PATTERN.matcher(distributionUri).matches();
}
@Nullable
private static String getRootProjectPath(@NotNull Module module) {
String externalSystemId = module.getOptionValue(ExternalSystemConstants.EXTERNAL_SYSTEM_ID_KEY);
if (externalSystemId == null || !GradleConstants.SYSTEM_ID.toString().equals(externalSystemId)) {
return null;
}
String path = module.getOptionValue(ExternalSystemConstants.ROOT_PROJECT_PATH_KEY);
return StringUtil.isEmpty(path) ? null : path;
}
}