| /* |
| * 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.groovy.mvc; |
| |
| import com.intellij.lang.properties.psi.PropertiesFile; |
| import com.intellij.openapi.application.AccessToken; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.application.WriteAction; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.module.ModifiableModuleModel; |
| import com.intellij.openapi.module.Module; |
| import com.intellij.openapi.module.ModuleManager; |
| import com.intellij.openapi.module.StdModuleTypes; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.roots.*; |
| import com.intellij.openapi.roots.impl.ModifiableModelCommitter; |
| import com.intellij.openapi.roots.libraries.Library; |
| import com.intellij.openapi.roots.libraries.LibraryTable; |
| import com.intellij.openapi.roots.libraries.LibraryUtil; |
| import com.intellij.openapi.roots.ui.configuration.actions.ModuleDeleteProvider; |
| import com.intellij.openapi.util.Comparing; |
| import com.intellij.openapi.util.Key; |
| import com.intellij.openapi.vfs.LocalFileSystem; |
| import com.intellij.openapi.vfs.VfsUtil; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.openapi.vfs.VirtualFileManager; |
| import com.intellij.psi.PsiFile; |
| import com.intellij.psi.PsiManager; |
| import com.intellij.util.ArrayUtil; |
| import com.intellij.util.CommonProcessors; |
| import com.intellij.util.Consumer; |
| import com.intellij.util.SmartList; |
| import com.intellij.util.containers.ContainerUtil; |
| import com.intellij.util.containers.MultiMap; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.jetbrains.jps.model.java.JavaSourceRootType; |
| import org.jetbrains.jps.model.module.JpsModuleSourceRootType; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.*; |
| |
| /** |
| * @author peter |
| */ |
| public class MvcModuleStructureUtil { |
| private static final Logger LOG = Logger.getInstance("#org.jetbrains.plugins.groovy.mvc.MvcModuleStructureUtil"); |
| @NonNls public static final String PLUGINS_DIRECTORY = "plugins"; |
| @NonNls public static final String APPLICATION_PROPERTIES = "application.properties"; |
| public static final Key<String> LAST_MVC_VERSION = Key.create("LAST_MVC_VERSION"); |
| |
| private MvcModuleStructureUtil() { |
| } |
| |
| @Nullable |
| public static ContentEntry findContentEntry(ModuleRootModel rootModel, VirtualFile root) { |
| for (ContentEntry entry : rootModel.getContentEntries()) { |
| if (Comparing.equal(entry.getFile(), root)) { |
| return entry; |
| } |
| } |
| |
| return null; |
| } |
| |
| @Nullable |
| private static Consumer<ModifiableRootModel> addSourceRootsAndLibDirectory(@NotNull final VirtualFile root, |
| final MvcProjectStructure structure) { |
| ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(structure.myModule); |
| |
| Map<VirtualFile, JpsModuleSourceRootType<?>> sourceRoots = new HashMap<VirtualFile, JpsModuleSourceRootType<?>>(); |
| for (ContentEntry entry : moduleRootManager.getContentEntries()) { |
| for (SourceFolder folder : entry.getSourceFolders()) { |
| sourceRoots.put(folder.getFile(), folder.getRootType()); |
| } |
| } |
| |
| root.refresh(false, true); |
| |
| final List<Consumer<ContentEntry>> actions = ContainerUtil.newArrayList(); |
| |
| for (Map.Entry<JpsModuleSourceRootType<?>, Collection<String>> entry : structure.getSourceFolders().entrySet()) { |
| JpsModuleSourceRootType<?> rootType = entry.getKey(); |
| |
| for (String src : entry.getValue()) { |
| addSourceFolder(root, src, rootType, actions, sourceRoots); |
| } |
| } |
| |
| for (final String src : structure.getInvalidSourceFolders()) { |
| removeSrcFolderFromRoots(root.findFileByRelativePath(src), actions, sourceRoots); |
| } |
| |
| for (final VirtualFile excluded : structure.getExcludedFolders(root)) { |
| if (moduleRootManager.getFileIndex().isInContent(excluded)) { |
| actions.add(new Consumer<ContentEntry>() { |
| @Override |
| public void consume(ContentEntry contentEntry) { |
| contentEntry.addExcludeFolder(excluded); |
| } |
| }); |
| } |
| } |
| |
| final Consumer<ModifiableRootModel> modifyLib = addJarDirectory(root, structure.myModule, structure.getUserLibraryName()); |
| |
| if (actions.isEmpty() && modifyLib == null && findContentEntry(moduleRootManager, root) != null) { |
| return null; |
| } |
| |
| return new Consumer<ModifiableRootModel>() { |
| @Override |
| public void consume(ModifiableRootModel model) { |
| ContentEntry contentEntry = findContentEntry(model, root); |
| if (contentEntry == null) { |
| contentEntry = model.addContentEntry(root); |
| } |
| |
| for (final Consumer<ContentEntry> action : actions) { |
| action.consume(contentEntry); |
| } |
| if (modifyLib != null) { |
| modifyLib.consume(model); |
| } |
| } |
| }; |
| } |
| |
| public static void removeSrcFolderFromRoots(final VirtualFile file, |
| @NotNull List<Consumer<ContentEntry>> actions, |
| @NotNull Map<VirtualFile, JpsModuleSourceRootType<?>> sourceRoots) { |
| removeSrcFolderFromRoots(file, actions, sourceRoots.keySet()); |
| } |
| |
| public static void removeSrcFolderFromRoots(final VirtualFile file, |
| @NotNull List<Consumer<ContentEntry>> actions, |
| @NotNull Collection<VirtualFile> sourceRoots) { |
| if (sourceRoots.contains(file)) { |
| actions.add(new Consumer<ContentEntry>() { |
| @Override |
| public void consume(ContentEntry contentEntry) { |
| SourceFolder[] folders = contentEntry.getSourceFolders(); |
| for (SourceFolder folder : folders) { |
| if (Comparing.equal(folder.getFile(), file)) { |
| contentEntry.removeSourceFolder(folder); |
| } |
| } |
| } |
| }); |
| } |
| } |
| |
| @Nullable |
| public static Consumer<ModifiableRootModel> addJarDirectory(VirtualFile root, Module module, final String libName) { |
| final VirtualFile libDir = root.findFileByRelativePath("lib"); |
| if (libDir == null || !libDir.isDirectory() || ProjectRootManager.getInstance(module.getProject()).getFileIndex().isExcluded(libDir)) { |
| return null; |
| } |
| |
| final Library library = findUserLibrary(module, libName); |
| if (library != null && library.isJarDirectory(libDir.getUrl())) { |
| return null; |
| } |
| |
| return new Consumer<ModifiableRootModel>() { |
| @Override |
| public void consume(ModifiableRootModel model) { |
| Library.ModifiableModel libModel = modifyDefaultLibrary(model, libName); |
| libModel.addJarDirectory(libDir, false); |
| libModel.commit(); |
| } |
| }; |
| } |
| |
| public static Library.ModifiableModel modifyDefaultLibrary(ModifiableRootModel model, String libName) { |
| LibraryTable libTable = model.getModuleLibraryTable(); |
| |
| for (Library library : libTable.getLibraries()) { |
| if (library.getName() != null && library.getName().startsWith(libName)) { |
| return library.getModifiableModel(); |
| } |
| } |
| |
| Library library = LibraryUtil.createLibrary(libTable, libName + " (" + model.getModule().getName() + ')'); |
| |
| for (OrderEntry entry : model.getOrderEntries()) { |
| if (!(entry instanceof LibraryOrderEntry)) continue; |
| |
| LibraryOrderEntry libraryEntry = (LibraryOrderEntry)entry; |
| if (libraryEntry.isModuleLevel() && libraryEntry.getLibrary() == library) { |
| libraryEntry.setExported(true); |
| } |
| } |
| |
| return library.getModifiableModel(); |
| } |
| |
| private static void addSourceFolder(@NotNull VirtualFile root, |
| @NotNull String relativePath, |
| final JpsModuleSourceRootType<?> rootType, |
| List<Consumer<ContentEntry>> actions, |
| Map<VirtualFile, JpsModuleSourceRootType<?>> sourceRoots) { |
| final VirtualFile src = root.findFileByRelativePath(relativePath); |
| if (src == null) { |
| return; |
| } |
| |
| JpsModuleSourceRootType<?> existingRootType = sourceRoots.get(src); |
| |
| if (rootType == JavaSourceRootType.TEST_SOURCE && (existingRootType != null && existingRootType != JavaSourceRootType.TEST_SOURCE)) { // see http://youtrack.jetbrains.net/issue/IDEA-70642 |
| actions.add(new Consumer<ContentEntry>() { |
| @Override |
| public void consume(ContentEntry entry) { |
| for (SourceFolder folder : entry.getSourceFolders()) { |
| if (Comparing.equal(folder.getFile(), src)) { |
| entry.removeSourceFolder(folder); |
| entry.addSourceFolder(src, rootType); |
| break; |
| } |
| } |
| } |
| }); |
| return; |
| } |
| |
| actions.add(new Consumer<ContentEntry>() { |
| @Override |
| public void consume(ContentEntry contentEntry) { |
| contentEntry.addSourceFolder(src, rootType); |
| } |
| }); |
| } |
| |
| public static void updateModuleStructure(Module module, MvcProjectStructure structure, @NotNull VirtualFile root) { |
| List<Consumer<ModifiableRootModel>> actions = getUpdateProjectStructureActions(Collections.singletonList(root), structure); |
| |
| if (!actions.isEmpty()) { |
| final ModifiableRootModel model = ModuleRootManager.getInstance(module).getModifiableModel(); |
| boolean success = false; |
| try { |
| for (final Consumer<ModifiableRootModel> action : actions) { |
| action.consume(model); |
| } |
| model.commit(); |
| success = true; |
| } |
| finally { |
| if (!success) { |
| model.dispose(); |
| } |
| } |
| } |
| } |
| |
| private static boolean checkValidity(VirtualFile pluginDir) { |
| pluginDir.refresh(false, false); |
| return pluginDir.isValid(); |
| } |
| |
| private static List<Consumer<ModifiableRootModel>> getUpdateProjectStructureActions(Collection<VirtualFile> appRoots, |
| MvcProjectStructure structure) { |
| for (final VirtualFile appRoot : ModuleRootManager.getInstance(structure.myModule).getContentRoots()) { |
| appRoot.refresh(false, false); |
| } |
| |
| List<Consumer<ModifiableRootModel>> actions = ContainerUtil.newArrayList(); |
| removeInvalidSourceRoots(actions, structure); |
| cleanupDefaultLibrary(structure.myModule, actions, appRoots, structure.getUserLibraryName()); |
| moveupLibrariesFromMavenPlugin(structure.myModule, actions); |
| |
| List<VirtualFile> rootsToFacetSetup = new ArrayList<VirtualFile>(appRoots.size()); |
| for (VirtualFile appRoot : appRoots) { |
| if (checkValidity(appRoot)) { |
| ContainerUtil.addIfNotNull(addSourceRootsAndLibDirectory(appRoot, structure), actions); |
| rootsToFacetSetup.add(appRoot); |
| } |
| } |
| |
| List<Runnable> facetActions = new ArrayList<Runnable>(); |
| structure.setupFacets(facetActions, rootsToFacetSetup); |
| for (final Runnable action : facetActions) { |
| actions.add(new Consumer<ModifiableRootModel>() { |
| @Override |
| public void consume(ModifiableRootModel model) { |
| action.run(); |
| } |
| }); |
| } |
| |
| return actions; |
| } |
| |
| @Nullable |
| private static OrderEntry[] moveupLibrariesFromMavenPlugin(ModuleRootModel moduleRootModel) { |
| LibraryOrderEntry newestLibrary = null; |
| int firstLibraryIndex = 0; |
| int newestLibraryIndex = 0; |
| |
| OrderEntry[] orderEntries = moduleRootModel.getOrderEntries(); |
| for (int i = 0; i < orderEntries.length; i++) { |
| if (orderEntries[i] instanceof LibraryOrderEntry) { |
| LibraryOrderEntry libraryEntry = (LibraryOrderEntry)orderEntries[i]; |
| String libraryName = libraryEntry.getLibraryName(); |
| if (libraryName != null && libraryName.contains("slf4j-api")) { |
| if (newestLibrary == null) { |
| newestLibrary = libraryEntry; |
| firstLibraryIndex = i; |
| newestLibraryIndex = i; |
| } |
| else { |
| if (libraryName.compareTo(newestLibrary.getLibraryName()) > 0) { |
| newestLibraryIndex = i; |
| newestLibrary = libraryEntry; |
| } |
| } |
| } |
| } |
| } |
| |
| if (firstLibraryIndex == newestLibraryIndex) return null; |
| |
| OrderEntry[] res = orderEntries.clone(); |
| ArrayUtil.swap(res, firstLibraryIndex, newestLibraryIndex); |
| return res; |
| } |
| |
| private static void moveupLibrariesFromMavenPlugin(final Module module, List<Consumer<ModifiableRootModel>> actions) { |
| if (moveupLibrariesFromMavenPlugin(ModuleRootManager.getInstance(module)) != null) { |
| actions.add(new Consumer<ModifiableRootModel>() { |
| @Override |
| public void consume(ModifiableRootModel modifiableRootModel) { |
| OrderEntry[] orderEntries = moveupLibrariesFromMavenPlugin(modifiableRootModel); |
| if (orderEntries != null) { |
| modifiableRootModel.rearrangeOrderEntries(orderEntries); |
| } |
| } |
| }); |
| } |
| } |
| |
| private static void removeInvalidSourceRoots(List<Consumer<ModifiableRootModel>> actions, MvcProjectStructure structure) { |
| final Set<SourceFolder> toRemove = ContainerUtil.newTroveSet(); |
| final Set<String> toRemoveContent = ContainerUtil.newTroveSet(); |
| for (ContentEntry entry : ModuleRootManager.getInstance(structure.myModule).getContentEntries()) { |
| final VirtualFile file = entry.getFile(); |
| if (file == null || !structure.isValidContentRoot(file)) { |
| toRemoveContent.add(entry.getUrl()); |
| } |
| else { |
| for (SourceFolder folder : entry.getSourceFolders()) { |
| if (folder.getFile() == null) { |
| toRemove.add(folder); |
| } |
| } |
| } |
| } |
| |
| if (!toRemove.isEmpty() || !toRemoveContent.isEmpty()) { |
| actions.add(new Consumer<ModifiableRootModel>() { |
| @Override |
| public void consume(ModifiableRootModel model) { |
| for (ContentEntry entry : model.getContentEntries()) { |
| if (toRemoveContent.remove(entry.getUrl())) { |
| model.removeContentEntry(entry); |
| } |
| else { |
| for (SourceFolder folder : entry.getSourceFolders()) { |
| if (toRemove.remove(folder)) { |
| entry.removeSourceFolder(folder); |
| } |
| } |
| } |
| } |
| } |
| }); |
| } |
| } |
| |
| public static void cleanupDefaultLibrary(Module module, |
| List<Consumer<ModifiableRootModel>> actions, |
| Collection<VirtualFile> appRoots, |
| final String libName) { |
| final Library library = findUserLibrary(module, libName); |
| if (library == null) { |
| return; |
| } |
| |
| final VirtualFileManager virtualFileManager = VirtualFileManager.getInstance(); |
| |
| final List<String> toRemoveUrls = new ArrayList<String>(); |
| |
| for (String url : library.getUrls(OrderRootType.CLASSES)) { |
| VirtualFile virtualFile = virtualFileManager.findFileByUrl(url); |
| |
| if (virtualFile == null) { |
| toRemoveUrls.add(url); |
| } |
| else { |
| if (library.isJarDirectory(url)) { |
| if (!virtualFile.getName().equals("lib") || !appRoots.contains(virtualFile.getParent())) { |
| toRemoveUrls.add(url); |
| } |
| } |
| } |
| } |
| |
| if (!toRemoveUrls.isEmpty()) { |
| actions.add(new Consumer<ModifiableRootModel>() { |
| @Override |
| public void consume(ModifiableRootModel model) { |
| final Library.ModifiableModel modifiableModel = modifyDefaultLibrary(model, libName); |
| for (String url : toRemoveUrls) { |
| modifiableModel.removeRoot(url, OrderRootType.CLASSES); |
| } |
| modifiableModel.commit(); |
| } |
| }); |
| } |
| } |
| |
| public static boolean hasModulesWithSupport(Project project, final MvcFramework framework) { |
| for (Module module : ModuleManager.getInstance(project).getModules()) { |
| if (framework.hasSupport(module)) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| public static List<Module> getAllModulesWithSupport(Project project, MvcFramework framework) { |
| List<Module> modules = new ArrayList<Module>(); |
| for (Module module : ModuleManager.getInstance(project).getModules()) { |
| if (framework.hasSupport(module)) { |
| modules.add(module); |
| } |
| } |
| return modules; |
| } |
| |
| @Nullable |
| private static Library extractNonModuleLibraries(List<Library> result, |
| ModuleRootManager rootManager, |
| boolean providedOnly, |
| String userLibraryName) { |
| Library userLibrary = null; |
| |
| for (OrderEntry entry : rootManager.getOrderEntries()) { |
| if (entry instanceof LibraryOrderEntry) { |
| LibraryOrderEntry libraryEntry = (LibraryOrderEntry)entry; |
| Library library = libraryEntry.getLibrary(); |
| |
| if (library != null) { |
| String libraryName = libraryEntry.getLibraryName(); |
| if (libraryName != null && libraryName.startsWith(userLibraryName)) { |
| userLibrary = library; |
| } |
| else { |
| if (library.getTable() != null && (!providedOnly || !libraryEntry.getScope().isForProductionRuntime())) { |
| result.add(library); |
| } |
| } |
| } |
| } |
| } |
| |
| return userLibrary; |
| } |
| |
| private static Set<String> getJarUrls(@Nullable Library library) { |
| if (library == null) return Collections.emptySet(); |
| |
| Set<String> res = new HashSet<String>(); |
| |
| for (String url : library.getUrls(OrderRootType.CLASSES)) { |
| if (!library.isJarDirectory(url)) { |
| res.add(url); |
| } |
| } |
| |
| return res; |
| } |
| |
| public static void syncAuxModuleSdk(@NotNull Module appModule, @NotNull Module pluginsModule, @NotNull final MvcFramework framework) { |
| final ModuleRootManager auxRootManager = ModuleRootManager.getInstance(pluginsModule); |
| final ModuleRootManager appRootManager = ModuleRootManager.getInstance(appModule); |
| |
| final boolean isSdkEquals = Comparing.equal(auxRootManager.getSdk(), appRootManager.getSdk()); |
| |
| List<Library> appLibraries = new ArrayList<Library>(); |
| Library appUserLibrary = extractNonModuleLibraries(appLibraries, appRootManager, false, framework.getUserLibraryName()); |
| |
| List<Library> auxLibraries = new ArrayList<Library>(); |
| Library auxUserLibrary = extractNonModuleLibraries(auxLibraries, auxRootManager, false, framework.getUserLibraryName()); |
| |
| final boolean isLibrariesEquals = appLibraries.equals(auxLibraries) && getJarUrls(auxUserLibrary).equals(getJarUrls(appUserLibrary)); |
| |
| if (!isSdkEquals || !isLibrariesEquals) { |
| AccessToken token = WriteAction.start(); |
| try { |
| final ModifiableRootModel model = auxRootManager.getModifiableModel(); |
| |
| if (!isSdkEquals) { |
| copySdk(appRootManager, model); |
| } |
| |
| if (!isLibrariesEquals) { |
| copyUserLibraries(appRootManager, model, framework); |
| } |
| |
| model.commit(); |
| } |
| finally { |
| token.finish(); |
| } |
| } |
| } |
| |
| @Nullable |
| public static PropertiesFile findApplicationProperties(@NotNull Module module, MvcFramework framework) { |
| VirtualFile root = framework.findAppRoot(module); |
| if (root == null) return null; |
| |
| VirtualFile appChild = root.findChild(APPLICATION_PROPERTIES); |
| if (appChild == null || !appChild.isValid()) return null; |
| |
| PsiManager manager = PsiManager.getInstance(module.getProject()); |
| PsiFile psiFile = manager.findFile(appChild); |
| if (psiFile instanceof PropertiesFile) { |
| return (PropertiesFile)psiFile; |
| } |
| return null; |
| } |
| |
| public static void removeAuxiliaryModule(Module toRemove) { |
| List<ModifiableRootModel> usingModels = new SmartList<ModifiableRootModel>(); |
| |
| Project project = toRemove.getProject(); |
| ModuleManager moduleManager = ModuleManager.getInstance(project); |
| |
| for (Module module : moduleManager.getModules()) { |
| if (module == toRemove) { |
| continue; |
| } |
| |
| ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module); |
| for (OrderEntry entry : moduleRootManager.getOrderEntries()) { |
| if (entry instanceof ModuleOrderEntry && toRemove == ((ModuleOrderEntry)entry).getModule()) { |
| usingModels.add(moduleRootManager.getModifiableModel()); |
| break; |
| } |
| } |
| } |
| |
| final ModifiableModuleModel moduleModel = moduleManager.getModifiableModel(); |
| |
| ModuleDeleteProvider.removeModule(toRemove, null, usingModels, moduleModel); |
| |
| ModifiableRootModel[] rootModels = usingModels.toArray(new ModifiableRootModel[usingModels.size()]); |
| ModifiableModelCommitter.multiCommit(rootModels, moduleModel); |
| } |
| |
| @NotNull |
| public static Module createAuxiliaryModule(@NotNull Module appModule, final String moduleName, final MvcFramework framework) { |
| ModuleManager moduleManager = ModuleManager.getInstance(appModule.getProject()); |
| final ModifiableModuleModel moduleModel = moduleManager.getModifiableModel(); |
| final String moduleFilePath = new File(appModule.getModuleFilePath()).getParent() + "/" + moduleName + ".iml"; |
| final VirtualFile existing = LocalFileSystem.getInstance().findFileByPath(moduleFilePath); |
| if (existing != null) { |
| try { |
| existing.delete("Grails/Griffon plugins maintenance"); |
| } |
| catch (IOException e) { |
| LOG.error(e); |
| } |
| } |
| |
| moduleModel.newModule(moduleFilePath, StdModuleTypes.JAVA.getId()); |
| moduleModel.commit(); |
| |
| Module pluginsModule = moduleManager.findModuleByName(moduleName); |
| assert pluginsModule != null; |
| |
| ModifiableRootModel newRootModel = ModuleRootManager.getInstance(pluginsModule).getModifiableModel(); |
| ModifiableRootModel appModel = ModuleRootManager.getInstance(appModule).getModifiableModel(); |
| |
| copySdkAndLibraries(appModel, newRootModel, framework); |
| |
| newRootModel.commit(); |
| appModel.commit(); |
| |
| return pluginsModule; |
| } |
| |
| public static void ensureDependency(@NotNull Module from, @NotNull Module to, boolean exported) { |
| if (!from.equals(to) && !hasDependency(from, to)) { |
| ModuleRootModificationUtil.addDependency(from, to, DependencyScope.COMPILE, exported); |
| } |
| } |
| |
| public static boolean hasDependency(@NotNull Module from, @NotNull Module to) { |
| for (OrderEntry entry : ModuleRootManager.getInstance(from).getOrderEntries()) { |
| if (entry instanceof ModuleOrderEntry) { |
| final ModuleOrderEntry moduleOrderEntry = (ModuleOrderEntry)entry; |
| if (to == moduleOrderEntry.getModule()) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| public static void removeDependency(@NotNull Module from, @NotNull Module to) { |
| if (!from.equals(to) && hasDependency(from, to)) { |
| final ModifiableRootModel fromModel = ModuleRootManager.getInstance(from).getModifiableModel(); |
| for (OrderEntry entry : fromModel.getOrderEntries()) { |
| if (entry instanceof ModuleOrderEntry) { |
| final ModuleOrderEntry moduleOrderEntry = (ModuleOrderEntry)entry; |
| if (to == moduleOrderEntry.getModule()) { |
| fromModel.removeOrderEntry(moduleOrderEntry); |
| } |
| } |
| } |
| fromModel.commit(); |
| } |
| } |
| |
| public static void copySdk(ModuleRootModel from, ModifiableRootModel to) { |
| if (from.isSdkInherited()) { |
| to.inheritSdk(); |
| } |
| else { |
| to.setSdk(from.getSdk()); |
| } |
| } |
| |
| public static void copySdkAndLibraries(ModuleRootModel from, ModifiableRootModel to, @NotNull MvcFramework framework) { |
| copySdk(from, to); |
| copyUserLibraries(from, to, framework); |
| } |
| |
| public static void copyUserLibraries(ModuleRootModel from, ModifiableRootModel to, @NotNull MvcFramework framework) { |
| Library userLibraryTo = null; |
| |
| for (OrderEntry entry : to.getOrderEntries()) { |
| if (entry instanceof LibraryOrderEntry) { |
| LibraryOrderEntry libraryEntry = (LibraryOrderEntry)entry; |
| Library library = libraryEntry.getLibrary(); |
| |
| if (library != null) { |
| String libraryName = libraryEntry.getLibraryName(); |
| if (libraryName != null && libraryName.startsWith(framework.getUserLibraryName())) { |
| userLibraryTo = library; |
| } |
| else { |
| if (library.getTable() != null && (!libraryEntry.getScope().isForProductionRuntime() || framework.isSDKLibrary(library))) { |
| to.removeOrderEntry(entry); |
| } |
| } |
| } |
| } |
| } |
| |
| Library userLibraryFrom = null; |
| |
| for (OrderEntry entry : from.getOrderEntries()) { |
| if (entry instanceof LibraryOrderEntry) { |
| LibraryOrderEntry libraryEntry = (LibraryOrderEntry)entry; |
| Library library = libraryEntry.getLibrary(); |
| |
| if (library != null) { |
| String libraryName = library.getName(); |
| if (libraryName != null && libraryName.startsWith(framework.getUserLibraryName())) { |
| userLibraryFrom = library; |
| } |
| else { |
| if (library.getTable() != null) { |
| LibraryOrderEntry libraryOrderEntry = to.addLibraryEntry(library); |
| libraryOrderEntry.setScope(DependencyScope.PROVIDED); |
| } |
| } |
| } |
| } |
| } |
| |
| if (userLibraryTo == null) { |
| if (userLibraryFrom == null) return; |
| |
| userLibraryTo = to.getModuleLibraryTable().createLibrary(framework.getUserLibraryName() + " (" + to.getModule().getName() + ')'); |
| } |
| else { |
| OrderEntry[] orderEntries = to.getOrderEntries().clone(); |
| |
| for (int i = 0; i < orderEntries.length; i++) { |
| OrderEntry orderEntry = orderEntries[i]; |
| if (orderEntry instanceof LibraryOrderEntry) { |
| if (userLibraryTo == ((LibraryOrderEntry)orderEntry).getLibrary()) { |
| System.arraycopy(orderEntries, i + 1, orderEntries, i, orderEntries.length - i - 1); |
| orderEntries[orderEntries.length - 1] = orderEntry; |
| to.rearrangeOrderEntries(orderEntries); |
| break; |
| } |
| } |
| } |
| } |
| |
| Library.ModifiableModel model = userLibraryTo.getModifiableModel(); |
| for (String url : model.getUrls(OrderRootType.CLASSES)) { |
| if (!model.isJarDirectory(url)) { |
| model.removeRoot(url, OrderRootType.CLASSES); |
| } |
| } |
| |
| if (userLibraryFrom != null) { |
| for (String url : userLibraryFrom.getUrls(OrderRootType.CLASSES)) { |
| if (!userLibraryFrom.isJarDirectory(url)) { |
| model.addRoot(url, OrderRootType.CLASSES); |
| } |
| } |
| } |
| |
| model.commit(); |
| } |
| |
| public static Consumer<ModifiableRootModel> removeStaleContentEntries(final Collection<VirtualFile> pluginDirs) { |
| return new Consumer<ModifiableRootModel>() { |
| @Override |
| public void consume(ModifiableRootModel modifiableRootModel) { |
| for (final ContentEntry entry : modifiableRootModel.getContentEntries()) { |
| if (!pluginDirs.contains(entry.getFile())) { |
| modifiableRootModel.removeContentEntry(entry); |
| } |
| } |
| } |
| }; |
| } |
| |
| public static void updateAuxModuleStructure(Module auxModule, Collection<VirtualFile> pluginDirs, MvcFramework framework) { |
| final MvcProjectStructure structure = framework.createProjectStructure(auxModule, true); |
| final List<Consumer<ModifiableRootModel>> actions = getUpdateProjectStructureActions(pluginDirs, structure); |
| for (final ContentEntry root : ModuleRootManager.getInstance(auxModule).getContentEntries()) { |
| if (!pluginDirs.contains(root.getFile())) { |
| actions.add(removeStaleContentEntries(pluginDirs)); |
| break; |
| } |
| } |
| |
| if (!actions.isEmpty()) { |
| actions.add(exportDefaultLibrary(structure.getUserLibraryName())); |
| } |
| |
| if (!actions.isEmpty()) { |
| final ModifiableRootModel model = ModuleRootManager.getInstance(auxModule).getModifiableModel(); |
| for (final Consumer<ModifiableRootModel> pluginsUpdateAction : actions) { |
| pluginsUpdateAction.consume(model); |
| } |
| model.commit(); |
| } |
| } |
| |
| public static Consumer<ModifiableRootModel> exportDefaultLibrary(final String libraryName) { |
| return new Consumer<ModifiableRootModel>() { |
| @Override |
| public void consume(ModifiableRootModel modifiableRootModel) { |
| for (final OrderEntry entry : modifiableRootModel.getOrderEntries()) { |
| if (entry instanceof LibraryOrderEntry) { |
| final LibraryOrderEntry libraryOrderEntry = (LibraryOrderEntry)entry; |
| String lName = libraryOrderEntry.getLibraryName(); |
| if (lName != null && lName.startsWith(libraryName)) { |
| libraryOrderEntry.setExported(true); |
| } |
| } |
| } |
| } |
| }; |
| } |
| |
| private static boolean hasChildDirectory(VirtualFile file) { |
| for (VirtualFile virtualFile : file.getChildren()) { |
| if (virtualFile.isDirectory()) return true; |
| } |
| |
| return false; |
| } |
| |
| public static void updateGlobalPluginModule(@NotNull Project project, @NotNull MvcFramework framework) { |
| MultiMap<VirtualFile, Module> map = new MultiMap<VirtualFile, Module>(); |
| |
| for (Module module : ModuleManager.getInstance(project).getModules()) { |
| if (framework.hasSupport(module)) { |
| VirtualFile globalPluginsDir = refreshAndFind(framework.getGlobalPluginsDir(module)); |
| if (globalPluginsDir != null && hasChildDirectory(globalPluginsDir)) { |
| map.putValue(globalPluginsDir, module); |
| } |
| } |
| } |
| |
| Map<VirtualFile, Module> globalAuxModules = new HashMap<VirtualFile, Module>(); |
| |
| for (Module module : ModuleManager.getInstance(project).getModules()) { |
| if (framework.isGlobalPluginModule(module)) { |
| VirtualFile[] contentRoots = ModuleRootManager.getInstance(module).getContentRoots(); |
| |
| VirtualFile parent = null; |
| |
| if (contentRoots.length > 0) { |
| parent = contentRoots[0].getParent(); |
| if (!map.containsKey(parent)) { |
| parent = null; |
| } |
| else { |
| for (int i = 1; i < contentRoots.length; i++) { |
| if (!Comparing.equal(parent, contentRoots[i].getParent())) { |
| parent = null; |
| break; |
| } |
| } |
| } |
| } |
| |
| if (parent == null) { |
| removeAuxiliaryModule(module); |
| } |
| else { |
| globalAuxModules.put(parent, module); |
| } |
| } |
| } |
| |
| for (VirtualFile virtualFile : map.keySet()) { |
| if (!globalAuxModules.containsKey(virtualFile)) { |
| Module appModule = map.get(virtualFile).iterator().next(); |
| Module module = |
| createAuxiliaryModule(appModule, generateUniqueModuleName(project, framework.getGlobalPluginsModuleName()), framework); |
| globalAuxModules.put(virtualFile, module); |
| } |
| } |
| |
| assert map.size() == globalAuxModules.size(); |
| |
| for (VirtualFile virtualFile : map.keySet()) { |
| List<VirtualFile> pluginRoots = new ArrayList<VirtualFile>(); |
| |
| for (VirtualFile child : virtualFile.getChildren()) { |
| if (child.isDirectory()) { |
| pluginRoots.add(child); |
| } |
| } |
| |
| assert !pluginRoots.isEmpty(); |
| |
| Module auxModule = globalAuxModules.get(virtualFile); |
| |
| updateAuxModuleStructure(auxModule, pluginRoots, framework); |
| |
| for (Module appModule : map.get(virtualFile)) { |
| ensureDependency(appModule, auxModule, false); |
| |
| Module commonPluginsModule = framework.findCommonPluginsModule(appModule); |
| if (commonPluginsModule != null) { |
| ensureDependency(commonPluginsModule, auxModule, false); |
| } |
| } |
| } |
| } |
| |
| private static String generateUniqueModuleName(@NotNull Project project, String prefix) { |
| ModuleManager manager = ModuleManager.getInstance(project); |
| int i = 0; |
| do { |
| String res = i == 0 ? prefix : prefix + i; |
| i++; |
| |
| if (manager.findModuleByName(res) == null) return res; |
| } |
| while (true); |
| } |
| |
| @Nullable |
| public static Module updateAuxiliaryPluginsModuleRoots(Module appModule, MvcFramework framework) { |
| Module commonPluginsModule = framework.findCommonPluginsModule(appModule); |
| |
| Set<VirtualFile> pluginRoots = new HashSet<VirtualFile>(); |
| |
| VirtualFile globalPluginsDir = refreshAndFind(framework.getGlobalPluginsDir(appModule)); |
| |
| for (VirtualFile pluginRoot : framework.getCommonPluginRoots(appModule, true)) { |
| if (checkValidity(pluginRoot)) { |
| if (globalPluginsDir == null || !VfsUtil.isAncestor(globalPluginsDir, pluginRoot, true)) { |
| pluginRoots.add(pluginRoot); |
| } |
| } |
| } |
| |
| if (pluginRoots.isEmpty()) { |
| if (commonPluginsModule != null) { |
| removeAuxiliaryModule(commonPluginsModule); |
| } |
| return null; |
| } |
| |
| if (commonPluginsModule == null) { |
| commonPluginsModule = createAuxiliaryModule(appModule, framework.getCommonPluginsModuleName(appModule), framework); |
| } |
| |
| ensureDependency(appModule, commonPluginsModule, false); |
| updateAuxModuleStructure(commonPluginsModule, pluginRoots, framework); |
| |
| return commonPluginsModule; |
| } |
| |
| public static Library findUserLibrary(@NotNull Module module, @NotNull final String name) { |
| CommonProcessors.FindProcessor<Library> processor = new CommonProcessors.FindProcessor<Library>() { |
| @Override |
| protected boolean accept(Library library) { |
| String libraryName = library.getName(); |
| return libraryName != null && libraryName.startsWith(name); |
| } |
| }; |
| |
| OrderEnumerator.orderEntries(module).forEachLibrary(processor); |
| |
| return processor.getFoundValue(); |
| } |
| |
| @Nullable |
| public static VirtualFile refreshAndFind(@Nullable File file) { |
| return findFile(file, true); |
| } |
| |
| @Nullable |
| public static VirtualFile findFile(@Nullable File file, boolean refresh) { |
| if (file == null) return null; |
| |
| if (refresh) { |
| ApplicationManager.getApplication().assertIsDispatchThread(); |
| return LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file); |
| } |
| else { |
| return LocalFileSystem.getInstance().findFileByIoFile(file); |
| } |
| } |
| |
| public static boolean isEnabledStructureUpdate() { |
| return !Boolean.parseBoolean(System.getProperty("grails.disable.structure.update")); |
| } |
| } |