| /* |
| * Copyright (C) 2013 The Android Open Source Project |
| * |
| * 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.android.tools.idea.gradle.project; |
| |
| import com.android.SdkConstants; |
| import com.android.tools.idea.gradle.GradleSyncState; |
| import com.android.tools.idea.gradle.facet.AndroidGradleFacet; |
| import com.android.tools.idea.gradle.parser.GradleSettingsFile; |
| import com.android.tools.idea.gradle.util.GradleUtil; |
| import com.google.common.base.Joiner; |
| import com.intellij.openapi.application.Result; |
| import com.intellij.openapi.command.WriteCommandAction; |
| import com.intellij.openapi.module.Module; |
| import com.intellij.openapi.module.ModuleManager; |
| import com.intellij.openapi.module.ModuleUtilCore; |
| import com.intellij.openapi.project.ModuleAdapter; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.io.FileUtil; |
| import com.intellij.openapi.vfs.VfsUtilCore; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.openapi.vfs.newvfs.BulkFileListener; |
| import com.intellij.openapi.vfs.newvfs.events.VFileEvent; |
| import com.intellij.openapi.vfs.newvfs.events.VFilePropertyChangeEvent; |
| import com.intellij.psi.PsiFile; |
| import org.jetbrains.annotations.NotNull; |
| |
| import java.util.List; |
| |
| /** |
| * GradleBuildFileUpdater listens for module-level events and updates the settings.gradle and build.gradle files to reflect any changes it |
| * sees. |
| */ |
| public class GradleBuildFileUpdater extends ModuleAdapter implements BulkFileListener { |
| private final Project myProject; |
| |
| public GradleBuildFileUpdater(@NotNull Project project) { |
| myProject = project; |
| } |
| |
| @Override |
| public void moduleAdded(@NotNull final Project project, @NotNull final Module module) { |
| // Don't do anything if we are in the middle of a project sync. |
| if (GradleSyncState.getInstance(project).isSyncInProgress()) { |
| return; |
| } |
| final GradleSettingsFile settingsFile = GradleSettingsFile.get(project); |
| if (settingsFile != null) { |
| // if settings.gradle does not have a module, we are in the middle of setting up a project. |
| final PsiFile psiFile = settingsFile.getPsiFile(); |
| Module found = ModuleUtilCore.findModuleForPsiElement(psiFile); |
| if (found != null) { |
| new WriteCommandAction<Void>(project, "Update settings.gradle", psiFile) { |
| @Override |
| protected void run(@NotNull Result<Void> result) throws Throwable { |
| settingsFile.addModule(module); |
| } |
| }.execute(); |
| } |
| } |
| } |
| |
| @Override |
| public void moduleRemoved(@NotNull Project project, @NotNull final Module module) { |
| // Don't do anything if we are in the middle of a project sync. |
| if (GradleSyncState.getInstance(project).isSyncInProgress()) { |
| return; |
| } |
| final GradleSettingsFile settingsFile = GradleSettingsFile.get(project); |
| if (settingsFile != null) { |
| new WriteCommandAction<Void>(project, "Update settings.gradle", settingsFile.getPsiFile()) { |
| @Override |
| protected void run(@NotNull Result<Void> result) throws Throwable { |
| settingsFile.removeModule(module); |
| } |
| }.execute(); |
| } |
| } |
| |
| @Override |
| public void before(@NotNull List<? extends VFileEvent> events) { |
| } |
| |
| /** |
| * This gets called on all file system changes, but we're interested in changes to module root directories. When we see them, we'll update |
| * the settings.gradle file. Note that users can also refactor modules by renaming them, which just changes their display name and not |
| * the filesystem directory -- when that happens, this class gets a |
| * {@link ModuleAdapter#modulesRenamed(com.intellij.openapi.project.Project, java.util.List)} callback. However, it's not appropriate to |
| * update settings.gradle in that case since Gradle doesn't case about IJ's display name of the module. |
| */ |
| @Override |
| public void after(@NotNull List<? extends VFileEvent> events) { |
| for (VFileEvent event : events) { |
| if (!(event instanceof VFilePropertyChangeEvent)) { |
| continue; |
| } |
| VFilePropertyChangeEvent propChangeEvent = (VFilePropertyChangeEvent)event; |
| if (!(VirtualFile.PROP_NAME.equals(propChangeEvent.getPropertyName()))) { |
| continue; |
| } |
| |
| VirtualFile eventFile = propChangeEvent.getFile(); |
| if (!eventFile.isDirectory()) { |
| continue; |
| } |
| |
| // Dig through our modules and find the one that matches the change event's path (the module will already have its path updated by |
| // now). |
| Module module = null; |
| Module[] modules = ModuleManager.getInstance(myProject).getModules(); |
| for (Module m : modules) { |
| VirtualFile file = GradleUtil.getGradleBuildFile(m); |
| if (file != null) { |
| VirtualFile moduleDir = file.getParent(); |
| if (moduleDir != null && FileUtil.pathsEqual(eventFile.getPath(), moduleDir.getPath())) { |
| module = m; |
| break; |
| } |
| } |
| } |
| |
| // If we found the module, then remove the old reference from the settings.gradle file and put in a new one. |
| if (module != null) { |
| AndroidGradleFacet androidGradleFacet = AndroidGradleFacet.getInstance(module); |
| if (androidGradleFacet == null) { |
| continue; |
| } |
| String oldPath = androidGradleFacet.getConfiguration().GRADLE_PROJECT_PATH; |
| String newPath = updateProjectNameInGradlePath(androidGradleFacet, eventFile); |
| |
| if (oldPath.equals(newPath)) { |
| continue; |
| } |
| |
| GradleSettingsFile settingsFile = GradleSettingsFile.get(myProject); |
| if (settingsFile != null) { |
| settingsFile.removeModule(oldPath); |
| settingsFile.addModule(newPath, VfsUtilCore.virtualToIoFile(eventFile)); |
| } |
| } |
| } |
| } |
| |
| @NotNull |
| private static String updateProjectNameInGradlePath(@NotNull AndroidGradleFacet androidGradleFacet, @NotNull VirtualFile moduleDir) { |
| String gradlePath = androidGradleFacet.getConfiguration().GRADLE_PROJECT_PATH; |
| if (gradlePath.equals(SdkConstants.GRADLE_PATH_SEPARATOR)) { |
| // This is root project, renaming folder does not affect it since the path is just ":". |
| return gradlePath; |
| } |
| List<String> pathSegments = GradleUtil.getPathSegments(gradlePath); |
| pathSegments.remove(pathSegments.size() - 1); |
| pathSegments.add(moduleDir.getName()); |
| |
| String newPath = Joiner.on(SdkConstants.GRADLE_PATH_SEPARATOR).join(pathSegments); |
| androidGradleFacet.getConfiguration().GRADLE_PROJECT_PATH = newPath; |
| return newPath; |
| } |
| } |