From f7b7e4cad75f95f9a519f24a080db1c8f8d74e8f Mon Sep 17 00:00:00 2001 From: "Denis.Zhdanov" Date: Wed, 8 Feb 2012 11:58:40 +0400 Subject: [PATCH] IDEA-76142: Gradle support - cannot update IDEA projects once one of build.gradle files changes 1. Introduced 'import gradle entity' infrastructure; 2. Implemented 'import gradle library dependency'; --- .../openapi/ui/SimpleToolWindowPanel.java | 4 +- .../resources/i18n/GradleBundle.properties | 2 + plugins/gradle/src/META-INF/plugin.xml | 20 ++- .../action/GradleImportEntityAction.java | 131 +++++++++++++++ .../plugins/gradle/diff/PlatformFacade.java | 4 + .../gradle/diff/PlatformFacadeImpl.java | 8 + .../importing/GradleContentRootImporter.java | 16 ++ .../GradleModuleDependencyImporter.java | 152 ++++++++++++++++++ .../importing/GradleModuleImporter.java | 125 ++++++++++++++ .../importing/GradleModulesImporter.java | 1 + .../GradleAdjustImportSettingsStep.java | 2 +- .../adjust/GradleProjectStructureFactory.java | 2 +- .../gradle/model/GradleDependency.java | 3 + .../gradle/model/GradleEntityType.java | 9 ++ .../GradleProjectStructureChangesPanel.java | 37 ++++- .../sync/GradleProjectStructureHelper.java | 83 ++++++---- .../sync/GradleProjectStructureTreeModel.java | 29 ++-- .../plugins/gradle/ui/GradleDataKeys.java | 18 +++ .../gradle/ui/GradleProjectStructureNode.java | 17 +- .../GradleProjectStructureNodeDescriptor.java | 7 +- .../plugins/gradle/util/GradleConstants.java | 11 +- 21 files changed, 616 insertions(+), 65 deletions(-) create mode 100644 plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleImportEntityAction.java create mode 100644 plugins/gradle/src/org/jetbrains/plugins/gradle/importing/GradleContentRootImporter.java create mode 100644 plugins/gradle/src/org/jetbrains/plugins/gradle/importing/GradleModuleDependencyImporter.java create mode 100644 plugins/gradle/src/org/jetbrains/plugins/gradle/importing/GradleModuleImporter.java create mode 100644 plugins/gradle/src/org/jetbrains/plugins/gradle/model/GradleEntityType.java create mode 100644 plugins/gradle/src/org/jetbrains/plugins/gradle/ui/GradleDataKeys.java diff --git a/platform/platform-api/src/com/intellij/openapi/ui/SimpleToolWindowPanel.java b/platform/platform-api/src/com/intellij/openapi/ui/SimpleToolWindowPanel.java index 5db53a0c3157..8198230777a4 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/SimpleToolWindowPanel.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/SimpleToolWindowPanel.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2012 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. @@ -24,6 +24,7 @@ import com.intellij.ui.switcher.QuickActionProvider; import com.intellij.util.ui.AwtVisitor; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; @@ -87,6 +88,7 @@ public class SimpleToolWindowPanel extends JPanel implements QuickActionProvider repaint(); } + @Nullable public Object getData(@NonNls String dataId) { return QuickActionProvider.KEY.is(dataId) && myProvideQuickActions ? this : null; } diff --git a/plugins/gradle/resources/i18n/GradleBundle.properties b/plugins/gradle/resources/i18n/GradleBundle.properties index 205b4ac96bb6..e85e37d53385 100644 --- a/plugins/gradle/resources/i18n/GradleBundle.properties +++ b/plugins/gradle/resources/i18n/GradleBundle.properties @@ -77,6 +77,8 @@ gradle.action.refresh.project.text=Refresh gradle project gradle.action.refresh.project.description=Allows to force linked gradle project refresh gradle.action.open.script.text=Open linked gradle project gradle.action.open.script.description=Allows to open project file of the linked gradle project at the editor +gradle.action.import.entity.text=Import +gradle.action.import.entity.description=Import target gradle entity gradle.settings.color.text.sample.conflict.node.name=node-with-conflicting-setup gradle.settings.color.text.sample.node.sync.name=node-with-same-setup diff --git a/plugins/gradle/src/META-INF/plugin.xml b/plugins/gradle/src/META-INF/plugin.xml index ca35a79bc1eb..ff4374eb52a2 100644 --- a/plugins/gradle/src/META-INF/plugin.xml +++ b/plugins/gradle/src/META-INF/plugin.xml @@ -34,17 +34,25 @@ - - + - + + + + + + + + + + @@ -72,10 +80,14 @@ + + + + \ No newline at end of file diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleImportEntityAction.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleImportEntityAction.java new file mode 100644 index 000000000000..5000c5995b7e --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleImportEntityAction.java @@ -0,0 +1,131 @@ +package org.jetbrains.plugins.gradle.action; + +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.actionSystem.PlatformDataKeys; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.gradle.config.GradleTextAttributes; +import org.jetbrains.plugins.gradle.importing.GradleModuleDependencyImporter; +import org.jetbrains.plugins.gradle.importing.GradleModuleImporter; +import org.jetbrains.plugins.gradle.model.GradleLibraryDependency; +import org.jetbrains.plugins.gradle.model.GradleLibraryDependencyId; +import org.jetbrains.plugins.gradle.model.GradleModule; +import org.jetbrains.plugins.gradle.sync.GradleProjectStructureHelper; +import org.jetbrains.plugins.gradle.ui.GradleDataKeys; +import org.jetbrains.plugins.gradle.ui.GradleProjectStructureNode; +import org.jetbrains.plugins.gradle.util.GradleBundle; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * Imports target {@link GradleTextAttributes#GRADLE_LOCAL_CHANGE 'gradle local'} entity to the current intellij project. + *

+ * Not thread-safe. + * + * @author Denis Zhdanov + * @since 2/7/12 10:32 AM + */ +public class GradleImportEntityAction extends AnAction { + + private static final Logger LOG = Logger.getInstance("#" + GradleImportEntityAction.class.getName()); + + public GradleImportEntityAction() { + getTemplatePresentation().setText(GradleBundle.message("gradle.action.import.entity.text")); + getTemplatePresentation().setDescription(GradleBundle.message("gradle.action.import.entity.description")); + } + + @Override + public void update(AnActionEvent e) { + final Collection> nodes = getInterestedNodes(e.getDataContext()); + e.getPresentation().setEnabled(!nodes.isEmpty()); + } + + @Override + public void actionPerformed(AnActionEvent e) { + final Project project = PlatformDataKeys.PROJECT.getData(e.getDataContext()); + if (project == null) { + LOG.warn("Can't import gradle-local entities. Reason: target intellij project is undefined"); + return; + } + + final Collection> nodes = getInterestedNodes(e.getDataContext()); + + // We need to import not only the selected nodes but their gradle-local parents as well. + List> nodesToImport = new ArrayList>(); + for (GradleProjectStructureNode node : nodes) { + collectHierarchyToImport(node, nodesToImport); + } + + GradleProjectStructureHelper projectStructureHelper = project.getComponent(GradleProjectStructureHelper.class); + GradleModuleImporter moduleImporter = ServiceManager.getService(GradleModuleImporter.class); + GradleModuleDependencyImporter dependencyImporter = ServiceManager.getService(GradleModuleDependencyImporter.class); + + for (GradleProjectStructureNode node : nodesToImport) { + switch (node.getType()) { + case MODULE: + final GradleModule gradleModule = projectStructureHelper.findGradleModuleByName(node.getDescriptor().getName()); + if (gradleModule != null) { + moduleImporter.importModule(gradleModule, project); + } + break; + case LIBRARY_DEPENDENCY: + final Object element = node.getDescriptor().getElement(); + if (!(element instanceof GradleLibraryDependencyId)) { + break; + } + GradleLibraryDependencyId id = (GradleLibraryDependencyId)element; + final GradleLibraryDependency dependency = projectStructureHelper.findLibraryDependency(id); + final Module intellijModule = projectStructureHelper.findIntellijModuleByName(id.getModuleName()); + if (dependency != null && intellijModule != null) { + dependencyImporter.importDependency(dependency, intellijModule); + } + break; + default: // Do nothing + } + } + } + + /** + * When particular gradle-local node is asked to be imported we need to import its gradle-local parent hierarchy as well. + *

+ * This method allows to collect all parent nodes of the given nodes that should be imported as well. + *

+ * Note: those nodes are added to the given collection starting from the topmost one. + * + * @param node target node + * @param storage target nodes storage + */ + private static void collectHierarchyToImport(@Nullable GradleProjectStructureNode node, + @NotNull Collection> storage) + { + if (node == null || node.getDescriptor().getAttributes() != GradleTextAttributes.GRADLE_LOCAL_CHANGE) { + return; + } + collectHierarchyToImport(node.getParent(), storage); + storage.add(node); + } + + @NotNull + private static Collection> getInterestedNodes(@NotNull DataContext context) { + final Collection> selectedNodes = GradleDataKeys.SYNC_TREE_NODE.getData(context); + if (selectedNodes == null) { + return Collections.emptyList(); + } + List> result = new ArrayList>(); + for (GradleProjectStructureNode node : selectedNodes) { + if (node.getDescriptor().getAttributes() == GradleTextAttributes.GRADLE_LOCAL_CHANGE) { + result.add(node); + } + } + return result; + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/diff/PlatformFacade.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/diff/PlatformFacade.java index 529fea913a98..bc8c9302a5a3 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/diff/PlatformFacade.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/diff/PlatformFacade.java @@ -3,6 +3,7 @@ package org.jetbrains.plugins.gradle.diff; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.OrderEntry; +import com.intellij.openapi.roots.libraries.LibraryTable; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.pom.java.LanguageLevel; import org.jetbrains.annotations.NotNull; @@ -28,6 +29,9 @@ import java.util.Collection; */ public interface PlatformFacade { + @NotNull + LibraryTable getProjectLibraryTable(@NotNull Project project); + @NotNull LanguageLevel getLanguageLevel(@NotNull Project project); diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/diff/PlatformFacadeImpl.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/diff/PlatformFacadeImpl.java index b4a82429a169..84ae46eda4a4 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/diff/PlatformFacadeImpl.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/diff/PlatformFacadeImpl.java @@ -8,6 +8,8 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.LanguageLevelProjectExtension; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.roots.OrderEntry; +import com.intellij.openapi.roots.impl.libraries.ProjectLibraryTable; +import com.intellij.openapi.roots.libraries.LibraryTable; import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.vfs.JarFileSystem; import com.intellij.openapi.vfs.VirtualFile; @@ -24,6 +26,12 @@ import java.util.Collection; */ public class PlatformFacadeImpl implements PlatformFacade { + @NotNull + @Override + public LibraryTable getProjectLibraryTable(@NotNull Project project) { + return ProjectLibraryTable.getInstance(project); + } + @NotNull @Override public LanguageLevel getLanguageLevel(@NotNull Project project) { diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/importing/GradleContentRootImporter.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/importing/GradleContentRootImporter.java new file mode 100644 index 000000000000..16e0baf4b191 --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/importing/GradleContentRootImporter.java @@ -0,0 +1,16 @@ +package org.jetbrains.plugins.gradle.importing; + +import com.intellij.openapi.module.Module; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.gradle.model.GradleContentRoot; + +/** + * @author Denis Zhdanov + * @since 2/7/12 3:20 PM + */ +public class GradleContentRootImporter { + + public void importContentRoots(@NotNull Iterable contentRoots, @NotNull Module module) { + // TODO den implement + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/importing/GradleModuleDependencyImporter.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/importing/GradleModuleDependencyImporter.java new file mode 100644 index 000000000000..ada5a3a5d389 --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/importing/GradleModuleDependencyImporter.java @@ -0,0 +1,152 @@ +package org.jetbrains.plugins.gradle.importing; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.roots.*; +import com.intellij.openapi.roots.libraries.Library; +import com.intellij.openapi.roots.libraries.LibraryTable; +import com.intellij.openapi.vfs.JarFileSystem; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.gradle.diff.PlatformFacade; +import org.jetbrains.plugins.gradle.model.*; +import org.jetbrains.plugins.gradle.util.GradleLog; + +import java.io.File; +import java.util.*; + +/** + * @author Denis Zhdanov + * @since 2/7/12 3:23 PM + */ +public class GradleModuleDependencyImporter { + + private static final Map LIBRARY_ROOT_MAPPINGS + = new EnumMap(LibraryPathType.class); + static { + LIBRARY_ROOT_MAPPINGS.put(LibraryPathType.BINARY, OrderRootType.CLASSES); + LIBRARY_ROOT_MAPPINGS.put(LibraryPathType.SOURCE, OrderRootType.SOURCES); + LIBRARY_ROOT_MAPPINGS.put(LibraryPathType.DOC, JavadocOrderRootType.getInstance()); + assert LibraryPathType.values().length == LIBRARY_ROOT_MAPPINGS.size(); + } + + @NotNull private final PlatformFacade myPlatformFacade; + + public GradleModuleDependencyImporter(@NotNull PlatformFacade platformFacade) { + myPlatformFacade = platformFacade; + } + + public void importDependency(@NotNull GradleDependency dependency, @NotNull Module module) { + importDependencies(Collections.singleton(dependency), module); + } + + public void importDependencies(@NotNull Iterable dependencies, @NotNull Module module) { + final List moduleDependencies = new ArrayList(); + final List libraryDependencies = new ArrayList(); + GradleEntityVisitor visitor = new GradleEntityVisitorAdapter() { + @Override + public void visit(@NotNull GradleModuleDependency dependency) { + moduleDependencies.add(dependency); + } + + @Override + public void visit(@NotNull GradleLibraryDependency dependency) { + libraryDependencies.add(dependency); + } + }; + for (GradleDependency dependency : dependencies) { + dependency.invite(visitor); + } + importLibraryDependencies(libraryDependencies, module); + importModuleDependencies(moduleDependencies, module); + } + + public void importModuleDependencies(@NotNull Iterable dependencies, @NotNull Module module) { + // TODO den implement + } + + public void importLibraryDependencies(@NotNull final Iterable dependencies, @NotNull final Module module) { + // TODO den make non-EDT agnostic + final LibraryTable libraryTable = myPlatformFacade.getProjectLibraryTable(module.getProject()); + final Map gradle2intellij = new HashMap(); + final Set librariesToCreate = new HashSet(); + for (final GradleLibraryDependency dependency : dependencies) { + // Try to find existing library in project libraries. + Library library = libraryTable.getLibraryByName(dependency.getName()); + if (library == null) { + librariesToCreate.add(dependency.getTarget()); + } + else { + gradle2intellij.put(dependency.getTarget(), library); + } + } + + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + // Create all necessary libraries. + if (!librariesToCreate.isEmpty()) { + final LibraryTable.ModifiableModel projectLibraryModel = libraryTable.getModifiableModel(); + try { + for (GradleLibrary library : librariesToCreate) { + final Library intellijLibrary = projectLibraryModel.createLibrary(library.getName()); + gradle2intellij.put(library, intellijLibrary); + final Library.ModifiableModel libraryModel = intellijLibrary.getModifiableModel(); + try { + registerPaths(library, libraryModel); + } + finally { + libraryModel.commit(); + } + } + } + finally { + projectLibraryModel.commit(); + } + } + + // Register library dependencies. + ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module); + final ModifiableRootModel moduleRootModel = moduleRootManager.getModifiableModel(); + try { + for (GradleLibraryDependency dependency : dependencies) { + LibraryOrderEntry orderEntry = moduleRootModel.addLibraryEntry(gradle2intellij.get(dependency.getTarget())); + orderEntry.setExported(dependency.isExported()); + orderEntry.setScope(dependency.getScope()); + } + } + finally { + moduleRootModel.commit(); + } + // TODO den refresh gradle project tree + } + }); + } + + private static void registerPaths(@NotNull GradleLibrary gradleLibrary, @NotNull Library.ModifiableModel model) { + for (LibraryPathType pathType : LibraryPathType.values()) { + for (String path : gradleLibrary.getPaths(pathType)) { + VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByIoFile(new File(path)); + if (virtualFile == null) { + GradleLog.LOG.warn(String.format("Can't find %s of the library '%s' at path '%s'", pathType, gradleLibrary.getName(), path)); + continue; + } + if (virtualFile.isDirectory()) { + model.addRoot(virtualFile, LIBRARY_ROOT_MAPPINGS.get(pathType)); + } + else { + VirtualFile jarRoot = JarFileSystem.getInstance().getJarRootForLocalFile(virtualFile); + if (jarRoot == null) { + GradleLog.LOG.warn(String.format( + "Can't parse contents of the jar file at path '%s' for the library '%s''", path, gradleLibrary.getName() + )); + continue; + } + model.addRoot(jarRoot, LIBRARY_ROOT_MAPPINGS.get(pathType)); + } + } + } + } + +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/importing/GradleModuleImporter.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/importing/GradleModuleImporter.java new file mode 100644 index 000000000000..7ee3a1eac434 --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/importing/GradleModuleImporter.java @@ -0,0 +1,125 @@ +package org.jetbrains.plugins.gradle.importing; + +import com.intellij.openapi.application.Application; +import com.intellij.openapi.application.ApplicationManager; +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.util.Alarm; +import com.intellij.util.containers.hash.HashMap; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.gradle.model.GradleModule; +import org.jetbrains.plugins.gradle.util.GradleLog; + +import java.io.File; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * Encapsulates functionality of importing gradle module to the intellij project. + * + * @author Denis Zhdanov + * @since 2/7/12 2:49 PM + */ +public class GradleModuleImporter { + + /** + * We can't modify project modules (add/remove) until it's initialised, so, we delay that activity. Current constant + * holds number of milliseconds to wait between 'after project initialisation' processing attempts. + */ + private static final int PROJECT_INITIALISATION_DELAY_MS = (int)TimeUnit.SECONDS.toMillis(1); + + private final Alarm myAlarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD); + + @NotNull private final GradleContentRootImporter myContentRootImporter; + @NotNull private final GradleModuleDependencyImporter myDependencyImporter; + + public GradleModuleImporter(@NotNull GradleContentRootImporter contentRootImporter, + @NotNull GradleModuleDependencyImporter dependencyImporter) + { + myContentRootImporter = contentRootImporter; + myDependencyImporter = dependencyImporter; + } + + public void importModule(@NotNull GradleModule module, @NotNull Project project) { + importModules(Collections.singleton(module), project, false); + } + + public void importModules(@NotNull final Iterable modules, @NotNull final Project project, final boolean recursive) { + if (!project.isInitialized()) { + myAlarm.addRequest(new ImportModulesTask(project, modules, recursive), PROJECT_INITIALISATION_DELAY_MS); + return; + } + UIUtil.invokeLaterIfNeeded(new Runnable() { + @Override + public void run() { + removeExistingModulesConfigs(modules); + Application application = ApplicationManager.getApplication(); + final Map moduleMappings = new HashMap(); + application.runWriteAction(new Runnable() { + @Override + public void run() { + final ModuleManager moduleManager = ModuleManager.getInstance(project); + for (GradleModule module : modules) { + final Module created = moduleManager.newModule(module.getModuleFilePath(), StdModuleTypes.JAVA); + moduleMappings.put(module, created); + } + } + }); + if (!recursive) { + return; + } + for (GradleModule gradleModule : modules) { + final Module intellijModule = moduleMappings.get(gradleModule); + myContentRootImporter.importContentRoots(gradleModule.getContentRoots(), intellijModule); + myDependencyImporter.importDependencies(gradleModule.getDependencies(), intellijModule); + } + } + }); + } + + private static void removeExistingModulesConfigs(@NotNull Iterable modules) { + for (GradleModule module : modules) { + // Remove existing '*.iml' file if necessary. + final String moduleFilePath = module.getModuleFilePath(); + File file = new File(moduleFilePath); + if (file.isFile()) { + boolean success = file.delete(); + if (!success) { + GradleLog.LOG.warn("Can't remove existing module file at '" + moduleFilePath + "'"); + } + } + } + } + + private class ImportModulesTask implements Runnable { + + private final Project myProject; + private final Iterable myModules; + private final boolean myRecursive; + + ImportModulesTask(@NotNull Project project, @NotNull Iterable modules, boolean recursive) { + myProject = project; + myModules = modules; + myRecursive = recursive; + } + + @Override + public void run() { + myAlarm.cancelAllRequests(); + if (!myProject.isInitialized()) { + myAlarm.addRequest( + new ImportModulesTask(myProject, myModules, myRecursive), + PROJECT_INITIALISATION_DELAY_MS + ); + return; + } + + importModules(myModules, myProject, myRecursive); + } + } + +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/importing/GradleModulesImporter.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/importing/GradleModulesImporter.java index 0753b80c148f..5a6de37bf1d0 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/importing/GradleModulesImporter.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/importing/GradleModulesImporter.java @@ -40,6 +40,7 @@ import java.util.concurrent.TimeUnit; * @author Denis Zhdanov * @since 8/26/11 10:01 AM */ +// TODO den remove public class GradleModulesImporter { private static final Map LIBRARY_ROOT_MAPPINGS diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/importing/wizard/adjust/GradleAdjustImportSettingsStep.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/importing/wizard/adjust/GradleAdjustImportSettingsStep.java index 4d6fb05e6100..f9bd8e7d8f6f 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/importing/wizard/adjust/GradleAdjustImportSettingsStep.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/importing/wizard/adjust/GradleAdjustImportSettingsStep.java @@ -260,7 +260,7 @@ public class GradleAdjustImportSettingsStep extends AbstractImportFromGradleWiza // We build tree node, its settings control and map them altogether. The only trick here is that nodes can reuse the same // settings control (e.g. more than one node may have the same library as a dependency, so, library dependency node for // every control will use the same settings control). - GradleProjectStructureNode result = new GradleProjectStructureNode(myFactory.buildDescriptor(entity)); + GradleProjectStructureNode result = new GradleProjectStructureNode(myFactory.buildDescriptor(entity), GradleEntityType.SYNTHETIC); Pair> pair = processed.get(entity); if (pair == null) { String cardName = String.valueOf(counter); diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/importing/wizard/adjust/GradleProjectStructureFactory.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/importing/wizard/adjust/GradleProjectStructureFactory.java index 95989623ae96..fef410ddc578 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/importing/wizard/adjust/GradleProjectStructureFactory.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/importing/wizard/adjust/GradleProjectStructureFactory.java @@ -138,7 +138,7 @@ public class GradleProjectStructureFactory { return; } for (GradleProjectStructureNode node : treeNodes) { - node.getDescriptor().setText(evt.getNewValue().toString()); + node.getDescriptor().setName(evt.getNewValue().toString()); model.nodeChanged(node); } } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/GradleDependency.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/GradleDependency.java index cc6558f17909..a20b775c2a06 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/GradleDependency.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/GradleDependency.java @@ -15,6 +15,9 @@ public interface GradleDependency extends GradleEntity { @NotNull DependencyScope getScope(); + + @NotNull + GradleModule getOwnerModule(); @NotNull GradleDependency clone(@NotNull GradleEntityCloneContext context); diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/GradleEntityType.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/GradleEntityType.java new file mode 100644 index 000000000000..948903544476 --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/GradleEntityType.java @@ -0,0 +1,9 @@ +package org.jetbrains.plugins.gradle.model; + +/** + * @author Denis Zhdanov + * @since 2/7/12 11:18 AM + */ +public enum GradleEntityType { + PROJECT, MODULE, LIBRARY_DEPENDENCY, SYNTHETIC +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/sync/GradleProjectStructureChangesPanel.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/sync/GradleProjectStructureChangesPanel.java index 72c5e424f100..ee03292076d2 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/sync/GradleProjectStructureChangesPanel.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/sync/GradleProjectStructureChangesPanel.java @@ -1,20 +1,27 @@ package org.jetbrains.plugins.gradle.sync; +import com.intellij.ide.ui.customization.CustomizationUtil; import com.intellij.openapi.project.Project; import com.intellij.ui.treeStructure.Tree; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.gradle.config.GradleToolWindowPanel; import org.jetbrains.plugins.gradle.diff.GradleProjectStructureChange; import org.jetbrains.plugins.gradle.diff.PlatformFacade; +import org.jetbrains.plugins.gradle.ui.GradleDataKeys; +import org.jetbrains.plugins.gradle.ui.GradleProjectStructureNode; import org.jetbrains.plugins.gradle.util.GradleConstants; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreePath; import java.awt.*; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; /** * UI control for showing difference between the gradle and intellij project structure. @@ -24,6 +31,7 @@ import java.util.Collection; */ public class GradleProjectStructureChangesPanel extends GradleToolWindowPanel { + private Tree myTree; private GradleProjectStructureTreeModel myTreeModel; private JPanel myContent; @@ -52,14 +60,16 @@ public class GradleProjectStructureChangesPanel extends GradleToolWindowPanel { private void init() { myContent = new JPanel(new GridBagLayout()); myTreeModel = new GradleProjectStructureTreeModel(getProject(), getProjectFacade(), getProjectStructureHelper()); - Tree tree = new Tree(myTreeModel); - applyInitialAppearance(tree, (DefaultMutableTreeNode)myTreeModel.getRoot()); + myTree = new Tree(myTreeModel); + applyInitialAppearance(myTree, (DefaultMutableTreeNode)myTreeModel.getRoot()); GridBagConstraints constraints = new GridBagConstraints(); constraints.fill = GridBagConstraints.BOTH; constraints.weightx = constraints.weighty = 1; - myContent.add(tree, constraints); - myContent.setBackground(tree.getBackground()); + myContent.add(myTree, constraints); + myContent.setBackground(myTree.getBackground()); + + CustomizationUtil.installPopupHandler(myTree, GradleConstants.ACTION_GROUP_SYNC_TREE, GradleConstants.SYNC_TREE_PLACE); } @NotNull @@ -75,6 +85,25 @@ public class GradleProjectStructureChangesPanel extends GradleToolWindowPanel { int i = 1; } + @Nullable + @Override + public Object getData(@NonNls String dataId) { + if (GradleDataKeys.SYNC_TREE_NODE.is(dataId)) { + TreePath[] paths = myTree.getSelectionPaths(); + if (paths == null) { + return null; + } + List> result = new ArrayList>(); + for (TreePath path : paths) { + result.add((GradleProjectStructureNode)path.getLastPathComponent()); + } + return result; + } + else { + return super.getData(dataId); + } + } + private static void applyInitialAppearance(@NotNull Tree tree, @NotNull DefaultMutableTreeNode node) { if (node.getUserObject() == GradleProjectStructureTreeModel.DEPENDENCIES_NODE_DESCRIPTOR) { tree.expandPath(new TreePath(node.getPath())); diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/sync/GradleProjectStructureHelper.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/sync/GradleProjectStructureHelper.java index 96b2c8d8ece0..8fba1b558e74 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/sync/GradleProjectStructureHelper.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/sync/GradleProjectStructureHelper.java @@ -8,6 +8,7 @@ import com.intellij.openapi.roots.OrderEntry; import com.intellij.openapi.roots.RootPolicy; import com.intellij.openapi.util.Ref; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.gradle.diff.PlatformFacade; import org.jetbrains.plugins.gradle.model.*; @@ -37,20 +38,20 @@ public class GradleProjectStructureHelper extends AbstractProjectComponent { * false otherwise */ public boolean isIntellijLibraryDependencyExist(@NotNull final GradleLibraryDependencyId id) { - for (Module module : myFacade.getModules(myProject)) { - if (!id.getModuleName().equals(module.getName())) { - continue; + final Module module = findIntellijModuleByName(id.getModuleName()); + if (module == null) { + return false; + } + + RootPolicy visitor = new RootPolicy() { + @Override + public Boolean visitLibraryOrderEntry(LibraryOrderEntry libraryOrderEntry, Boolean value) { + return id.getLibraryName().equals(libraryOrderEntry.getLibraryName()); } - RootPolicy visitor = new RootPolicy() { - @Override - public Boolean visitLibraryOrderEntry(LibraryOrderEntry libraryOrderEntry, Boolean value) { - return id.getLibraryName().equals(libraryOrderEntry.getLibraryName()); - } - }; - for (OrderEntry entry : myFacade.getOrderEntries(module)) { - if (entry.accept(visitor, false)) { - return true; - } + }; + for (OrderEntry entry : myFacade.getOrderEntries(module)) { + if (entry.accept(visitor, false)) { + return true; } } return false; @@ -64,29 +65,55 @@ public class GradleProjectStructureHelper extends AbstractProjectComponent { * false otherwise */ public boolean isGradleLibraryDependencyExist(@NotNull final GradleLibraryDependencyId id) { + return findLibraryDependency(id) != null; + } + + @Nullable + public Module findIntellijModuleByName(@NotNull String name) { + for (Module module : myFacade.getModules(myProject)) { + if (name.equals(module.getName())) { + return module; + } + } + return null; + } + + @Nullable + public GradleModule findGradleModuleByName(@NotNull String name) { final GradleProject project = myModel.getGradleProject(); if (project == null) { - return false; + return null; } - final Ref matched = new Ref(); + for (GradleModule module : project.getModules()) { + if (name.equals(module.getName())) { + return module; + } + } + return null; + } + + @Nullable + public GradleLibraryDependency findLibraryDependency(@NotNull final GradleLibraryDependencyId id) { + final GradleModule module = findGradleModuleByName(id.getModuleName()); + if (module == null) { + return null; + } + final Ref ref = new Ref(); GradleEntityVisitor visitor = new GradleEntityVisitorAdapter() { @Override public void visit(@NotNull GradleLibraryDependency dependency) { - matched.set(id.getLibraryName().equals(dependency.getName())); - } - }; - for (GradleModule module : project.getModules()) { - if (!id.getModuleName().equals(module.getName())) { - continue; - } - for (GradleDependency dependency : module.getDependencies()) { - dependency.invite(visitor); - if (matched.get()) { - return true; + if (id.getLibraryName().equals(dependency.getName())) { + ref.set(dependency); } } - return false; + }; + for (GradleDependency dependency : module.getDependencies()) { + dependency.invite(visitor); + final GradleLibraryDependency result = ref.get(); + if (result != null) { + return result; + } } - return false; + return null; } } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/sync/GradleProjectStructureTreeModel.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/sync/GradleProjectStructureTreeModel.java index 9374826609b2..9a32b95e3338 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/sync/GradleProjectStructureTreeModel.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/sync/GradleProjectStructureTreeModel.java @@ -10,6 +10,7 @@ import com.intellij.util.containers.hash.HashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.gradle.config.GradleTextAttributes; import org.jetbrains.plugins.gradle.diff.*; +import org.jetbrains.plugins.gradle.model.GradleEntityType; import org.jetbrains.plugins.gradle.model.GradleLibraryDependencyId; import org.jetbrains.plugins.gradle.ui.GradleIcons; import org.jetbrains.plugins.gradle.ui.GradleProjectStructureNode; @@ -77,7 +78,8 @@ public class GradleProjectStructureTreeModel extends DefaultTreeModel { myModuleDependencies.clear(); myModules.clear(); - GradleProjectStructureNode root = buildNode(getProject(), getProject().getName(), myPlatformFacade.getProjectIcon()); + GradleProjectStructureNode root + = buildNode(getProject(), GradleEntityType.PROJECT, getProject().getName(), myPlatformFacade.getProjectIcon()); final Collection modules = myPlatformFacade.getModules(getProject()); RootPolicy policy = new RootPolicy() { @Override @@ -86,7 +88,7 @@ public class GradleProjectStructureTreeModel extends DefaultTreeModel { } }; for (Module module : modules) { - final GradleProjectStructureNode moduleNode = buildNode(module.getName(), GradleIcons.MODULE_ICON); + final GradleProjectStructureNode moduleNode = buildNode(GradleEntityType.MODULE, module.getName(), GradleIcons.MODULE_ICON); myModules.put(module.getName(), moduleNode); // Assuming that module names are unique. List libraryDependencies = new ArrayList(); for (OrderEntry orderEntry : myPlatformFacade.getOrderEntries(module)) { @@ -100,7 +102,7 @@ public class GradleProjectStructureTreeModel extends DefaultTreeModel { if (id == null) { continue; } - dependenciesNode.add(buildNode(id, id.getLibraryName(), GradleIcons.LIB_ICON)); + dependenciesNode.add(buildNode(id, GradleEntityType.LIBRARY_DEPENDENCY, id.getLibraryName(), GradleIcons.LIB_ICON)); } moduleNode.add(dependenciesNode); } @@ -119,14 +121,18 @@ public class GradleProjectStructureTreeModel extends DefaultTreeModel { return new GradleProjectStructureNodeDescriptor(entity, name, icon); } - private static GradleProjectStructureNode buildNode(@NotNull String name, @NotNull Icon icon) { - return buildNode(name, name, icon); + private static GradleProjectStructureNode buildNode(@NotNull GradleEntityType type, @NotNull String name, @NotNull Icon icon) { + return buildNode(name, type, name, icon); } - private static GradleProjectStructureNode buildNode(@NotNull T entity, @NotNull String name, @NotNull Icon icon) { - return new GradleProjectStructureNode(buildDescriptor(entity, name, icon)); + private static GradleProjectStructureNode buildNode(@NotNull T entity, + @NotNull GradleEntityType type, + @NotNull String name, + @NotNull Icon icon) + { + return new GradleProjectStructureNode(buildDescriptor(entity, name, icon), type); } - + private GradleProjectStructureNode getDependenciesNode(@NotNull String moduleName) { final GradleProjectStructureNode cached = myModuleDependencies.get(moduleName); if (cached != null) { @@ -134,11 +140,11 @@ public class GradleProjectStructureTreeModel extends DefaultTreeModel { } GradleProjectStructureNode moduleNode = myModules.get(moduleName); if (moduleNode == null) { - moduleNode = buildNode(moduleName, GradleIcons.MODULE_ICON); + moduleNode = buildNode(GradleEntityType.MODULE, moduleName, GradleIcons.MODULE_ICON); myModules.put(moduleName, moduleNode); } - GradleProjectStructureNode result = new GradleProjectStructureNode(DEPENDENCIES_NODE_DESCRIPTOR); + GradleProjectStructureNode result = new GradleProjectStructureNode(DEPENDENCIES_NODE_DESCRIPTOR, GradleEntityType.SYNTHETIC); moduleNode.add(result); myModuleDependencies.put(moduleName, result); @@ -184,7 +190,8 @@ public class GradleProjectStructureTreeModel extends DefaultTreeModel { return; } } - GradleProjectStructureNode newNode = buildNode(id, id.getLibraryName(), GradleIcons.LIB_ICON); + GradleProjectStructureNode newNode + = buildNode(id, GradleEntityType.LIBRARY_DEPENDENCY, id.getLibraryName(), GradleIcons.LIB_ICON); newNode.getDescriptor().setAttributes(attributes); dependenciesNode.add(newNode); nodeStructureChanged(dependenciesNode); diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/ui/GradleDataKeys.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/ui/GradleDataKeys.java new file mode 100644 index 000000000000..134db91a6efc --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/ui/GradleDataKeys.java @@ -0,0 +1,18 @@ +package org.jetbrains.plugins.gradle.ui; + +import com.intellij.openapi.actionSystem.DataKey; + +import java.util.Collection; + +/** + * @author Denis Zhdanov + * @since 2/7/12 11:19 AM + */ +public class GradleDataKeys { + + /** Key for obtaining currently selected nodes at the gradle 'sync project structure' tree. */ + public static final DataKey>> SYNC_TREE_NODE = DataKey.create("gradle.sync.tree.node"); + + private GradleDataKeys() { + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/ui/GradleProjectStructureNode.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/ui/GradleProjectStructureNode.java index 2551a3162a2e..e009d1a88b19 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/ui/GradleProjectStructureNode.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/ui/GradleProjectStructureNode.java @@ -3,6 +3,7 @@ package org.jetbrains.plugins.gradle.ui; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.gradle.config.GradleTextAttributes; import org.jetbrains.plugins.gradle.diff.GradleProjectStructureChange; +import org.jetbrains.plugins.gradle.model.GradleEntityType; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.MutableTreeNode; @@ -16,11 +17,14 @@ import java.util.*; public class GradleProjectStructureNode extends DefaultMutableTreeNode implements Iterable> { private final Set myConflictChanges = new HashSet(); - private final GradleProjectStructureNodeDescriptor myDescriptor; - public GradleProjectStructureNode(@NotNull GradleProjectStructureNodeDescriptor descriptor) { + private final GradleProjectStructureNodeDescriptor myDescriptor; + private final GradleEntityType myType; + + public GradleProjectStructureNode(@NotNull GradleProjectStructureNodeDescriptor descriptor, @NotNull GradleEntityType type) { super(descriptor); myDescriptor = descriptor; + myType = type; } @NotNull @@ -28,6 +32,11 @@ public class GradleProjectStructureNode extends DefaultMutableTreeNode implem return myDescriptor; } + @NotNull + public GradleEntityType getType() { + return myType; + } + @Override public GradleProjectStructureNode getChildAt(int index) { return (GradleProjectStructureNode)super.getChildAt(index); @@ -41,10 +50,10 @@ public class GradleProjectStructureNode extends DefaultMutableTreeNode implem @Override public void add(MutableTreeNode newChild) { GradleProjectStructureNode child = (GradleProjectStructureNode)newChild; - final String newText = child.getDescriptor().getText(); + final String newName = child.getDescriptor().getName(); for (int i = 0; i < getChildCount(); i++) { GradleProjectStructureNode node = getChildAt(i); - if (newText.compareTo(node.getDescriptor().getText()) < 0) { + if (newName.compareTo(node.getDescriptor().getName()) < 0) { insert(newChild, i); return; } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/ui/GradleProjectStructureNodeDescriptor.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/ui/GradleProjectStructureNodeDescriptor.java index 6fe8baa490f0..031e5077fa2e 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/ui/GradleProjectStructureNodeDescriptor.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/ui/GradleProjectStructureNodeDescriptor.java @@ -46,12 +46,7 @@ public class GradleProjectStructureNodeDescriptor extends PresentableNodeDesc return myData; } - @NotNull - public String getText() { - return myName; - } - - public void setText(@NotNull String name) { + public void setName(@NotNull String name) { myName = name; } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleConstants.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleConstants.java index 49f441744592..acc24a256475 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleConstants.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleConstants.java @@ -1,10 +1,7 @@ package org.jetbrains.plugins.gradle.util; -import com.intellij.openapi.util.IconLoader; import org.jetbrains.annotations.NonNls; -import javax.swing.*; - /** * Holds object representation of icons used at the Gradle plugin. * @@ -13,9 +10,13 @@ import javax.swing.*; */ public class GradleConstants { - @NonNls public static final String EXTENSION = "gradle"; - @NonNls public static final String DEFAULT_SCRIPT_NAME = "build.gradle"; + @NonNls public static final String EXTENSION = "gradle"; + @NonNls public static final String DEFAULT_SCRIPT_NAME = "build.gradle"; + @NonNls public static final String TOOL_WINDOW_TOOLBAR_PLACE = "GRADLE_SYNC_CHANGES_TOOLBAR"; + @NonNls public static final String SYNC_TREE_PLACE = "GRADLE_SYNC_TREE_PLACE"; + + @NonNls public static final String ACTION_GROUP_SYNC_TREE = "Gradle.SyncTreeGroup"; private GradleConstants() { }