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';
This commit is contained in:
Denis.Zhdanov
2012-02-08 12:00:33 +04:00
parent 7b29634fb8
commit f7b7e4cad7
21 changed files with 616 additions and 65 deletions
@@ -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;
}
@@ -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
+16 -4
View File
@@ -34,17 +34,25 @@
<colorAndFontPanelFactory implementation="org.jetbrains.plugins.gradle.config.GradleColorAndFontPanelFactory"/>
<colorAndFontDescriptorProvider implementation="org.jetbrains.plugins.gradle.config.GradleColorAndFontDescriptorsProvider"/>
<applicationService serviceImplementation="org.jetbrains.plugins.gradle.remote.GradleApiFacadeManager"/>
<applicationService serviceImplementation="org.jetbrains.plugins.gradle.util.GradleLibraryManager"/>
<!--Structure diff calculators-->
<applicationService serviceInterface="org.jetbrains.plugins.gradle.diff.GradleStructureChangesCalculator"
serviceImplementation="org.jetbrains.plugins.gradle.diff.GradleProjectStructureChangesCalculator"/>
<applicationService serviceImplementation="org.jetbrains.plugins.gradle.diff.GradleModuleStructureChangesCalculator"/>
<applicationService serviceImplementation="org.jetbrains.plugins.gradle.diff.GradleLibraryStructureChangesCalculator"/>
<applicationService serviceImplementation="org.jetbrains.plugins.gradle.diff.GradleLibraryDependencyStructureChangesCalculator"/>
<applicationService serviceInterface="org.jetbrains.plugins.gradle.diff.PlatformFacade"
serviceImplementation="org.jetbrains.plugins.gradle.diff.PlatformFacadeImpl"/>
<!--Import services-->
<applicationService serviceImplementation="org.jetbrains.plugins.gradle.importing.GradleModuleImporter"/>
<applicationService serviceImplementation="org.jetbrains.plugins.gradle.importing.GradleContentRootImporter"/>
<applicationService serviceImplementation="org.jetbrains.plugins.gradle.importing.GradleModuleDependencyImporter"/>
<!--Generic application services-->
<applicationService serviceImplementation="org.jetbrains.plugins.gradle.remote.GradleApiFacadeManager"/>
<applicationService serviceImplementation="org.jetbrains.plugins.gradle.util.GradleLibraryManager"/>
<applicationService serviceInterface="org.jetbrains.plugins.gradle.notification.GradleProgressNotificationManager"
serviceImplementation="org.jetbrains.plugins.gradle.notification.GradleProgressNotificationManagerImpl"/>
<applicationService serviceInterface="org.jetbrains.plugins.gradle.diff.PlatformFacade"
serviceImplementation="org.jetbrains.plugins.gradle.diff.PlatformFacadeImpl"/>
<projectService serviceImplementation="org.jetbrains.plugins.gradle.config.GradleSettings"/>
<projectService serviceImplementation="org.jetbrains.plugins.gradle.config.GradleProjectState"/>
@@ -72,10 +80,14 @@
<action id="Gradle.LinkToProject" class="org.jetbrains.plugins.gradle.action.GradleLinkToProjectAction" icon="/general/add.png"/>
<action id="Gradle.RefreshProject" class="org.jetbrains.plugins.gradle.action.GradleRefreshProjectAction" icon="/actions/sync.png"/>
<action id="Gradle.OpenScript" class="org.jetbrains.plugins.gradle.action.GradleOpenScriptAction" icon="/icons/gradle.png"/>
<action id="Gradle.ImportEntity" class="org.jetbrains.plugins.gradle.action.GradleImportEntityAction"/>
<group id="Gradle.ChangeActionsToolbar">
<reference id="Gradle.RefreshProject"/>
<reference id="Gradle.OpenScript"/>
</group>
<group id="Gradle.SyncTreeGroup">
<reference id="Gradle.ImportEntity"/>
</group>
</actions>
</idea-plugin>
@@ -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.
* <p/>
* 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<GradleProjectStructureNode<?>> 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<GradleProjectStructureNode<?>> nodes = getInterestedNodes(e.getDataContext());
// We need to import not only the selected nodes but their gradle-local parents as well.
List<GradleProjectStructureNode<?>> nodesToImport = new ArrayList<GradleProjectStructureNode<?>>();
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.
* <p/>
* This method allows to collect all parent nodes of the given nodes that should be imported as well.
* <p/>
* <b>Note:</b> 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<GradleProjectStructureNode<?>> storage)
{
if (node == null || node.getDescriptor().getAttributes() != GradleTextAttributes.GRADLE_LOCAL_CHANGE) {
return;
}
collectHierarchyToImport(node.getParent(), storage);
storage.add(node);
}
@NotNull
private static Collection<GradleProjectStructureNode<?>> getInterestedNodes(@NotNull DataContext context) {
final Collection<GradleProjectStructureNode<?>> selectedNodes = GradleDataKeys.SYNC_TREE_NODE.getData(context);
if (selectedNodes == null) {
return Collections.emptyList();
}
List<GradleProjectStructureNode<?>> result = new ArrayList<GradleProjectStructureNode<?>>();
for (GradleProjectStructureNode<?> node : selectedNodes) {
if (node.getDescriptor().getAttributes() == GradleTextAttributes.GRADLE_LOCAL_CHANGE) {
result.add(node);
}
}
return result;
}
}
@@ -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);
@@ -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) {
@@ -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<GradleContentRoot> contentRoots, @NotNull Module module) {
// TODO den implement
}
}
@@ -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<LibraryPathType, OrderRootType> LIBRARY_ROOT_MAPPINGS
= new EnumMap<LibraryPathType, OrderRootType>(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<GradleDependency> dependencies, @NotNull Module module) {
final List<GradleModuleDependency> moduleDependencies = new ArrayList<GradleModuleDependency>();
final List<GradleLibraryDependency> libraryDependencies = new ArrayList<GradleLibraryDependency>();
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<GradleModuleDependency> dependencies, @NotNull Module module) {
// TODO den implement
}
public void importLibraryDependencies(@NotNull final Iterable<GradleLibraryDependency> dependencies, @NotNull final Module module) {
// TODO den make non-EDT agnostic
final LibraryTable libraryTable = myPlatformFacade.getProjectLibraryTable(module.getProject());
final Map<GradleLibrary, Library> gradle2intellij = new HashMap<GradleLibrary, Library>();
final Set<GradleLibrary> librariesToCreate = new HashSet<GradleLibrary>();
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));
}
}
}
}
}
@@ -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<GradleModule> 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<GradleModule, Module> moduleMappings = new HashMap<GradleModule, Module>();
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<GradleModule> 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<GradleModule> myModules;
private final boolean myRecursive;
ImportModulesTask(@NotNull Project project, @NotNull Iterable<GradleModule> 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);
}
}
}
@@ -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<LibraryPathType, OrderRootType> LIBRARY_ROOT_MAPPINGS
@@ -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<T> result = new GradleProjectStructureNode<T>(myFactory.buildDescriptor(entity));
GradleProjectStructureNode<T> result = new GradleProjectStructureNode<T>(myFactory.buildDescriptor(entity), GradleEntityType.SYNTHETIC);
Pair<String, Collection<GradleProjectStructureNode>> pair = processed.get(entity);
if (pair == null) {
String cardName = String.valueOf(counter);
@@ -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);
}
}
@@ -15,6 +15,9 @@ public interface GradleDependency extends GradleEntity {
@NotNull
DependencyScope getScope();
@NotNull
GradleModule getOwnerModule();
@NotNull
GradleDependency clone(@NotNull GradleEntityCloneContext context);
@@ -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
}
@@ -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<GradleProjectStructureNode<?>> result = new ArrayList<GradleProjectStructureNode<?>>();
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()));
@@ -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 {
* <code>false</code> 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<Boolean> visitor = new RootPolicy<Boolean>() {
@Override
public Boolean visitLibraryOrderEntry(LibraryOrderEntry libraryOrderEntry, Boolean value) {
return id.getLibraryName().equals(libraryOrderEntry.getLibraryName());
}
RootPolicy<Boolean> visitor = new RootPolicy<Boolean>() {
@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 {
* <code>false</code> 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<Boolean> matched = new Ref<Boolean>();
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<GradleLibraryDependency> ref = new Ref<GradleLibraryDependency>();
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;
}
}
@@ -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<Project> root = buildNode(getProject(), getProject().getName(), myPlatformFacade.getProjectIcon());
GradleProjectStructureNode<Project> root
= buildNode(getProject(), GradleEntityType.PROJECT, getProject().getName(), myPlatformFacade.getProjectIcon());
final Collection<Module> modules = myPlatformFacade.getModules(getProject());
RootPolicy<LibraryOrderEntry> policy = new RootPolicy<LibraryOrderEntry>() {
@Override
@@ -86,7 +88,7 @@ public class GradleProjectStructureTreeModel extends DefaultTreeModel {
}
};
for (Module module : modules) {
final GradleProjectStructureNode<String> moduleNode = buildNode(module.getName(), GradleIcons.MODULE_ICON);
final GradleProjectStructureNode<String> moduleNode = buildNode(GradleEntityType.MODULE, module.getName(), GradleIcons.MODULE_ICON);
myModules.put(module.getName(), moduleNode); // Assuming that module names are unique.
List<LibraryOrderEntry> libraryDependencies = new ArrayList<LibraryOrderEntry>();
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<T>(entity, name, icon);
}
private static GradleProjectStructureNode<String> buildNode(@NotNull String name, @NotNull Icon icon) {
return buildNode(name, name, icon);
private static GradleProjectStructureNode<String> buildNode(@NotNull GradleEntityType type, @NotNull String name, @NotNull Icon icon) {
return buildNode(name, type, name, icon);
}
private static <T> GradleProjectStructureNode<T> buildNode(@NotNull T entity, @NotNull String name, @NotNull Icon icon) {
return new GradleProjectStructureNode<T>(buildDescriptor(entity, name, icon));
private static <T> GradleProjectStructureNode<T> buildNode(@NotNull T entity,
@NotNull GradleEntityType type,
@NotNull String name,
@NotNull Icon icon)
{
return new GradleProjectStructureNode<T>(buildDescriptor(entity, name, icon), type);
}
private GradleProjectStructureNode<String> getDependenciesNode(@NotNull String moduleName) {
final GradleProjectStructureNode<String> cached = myModuleDependencies.get(moduleName);
if (cached != null) {
@@ -134,11 +140,11 @@ public class GradleProjectStructureTreeModel extends DefaultTreeModel {
}
GradleProjectStructureNode<String> 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<String> result = new GradleProjectStructureNode<String>(DEPENDENCIES_NODE_DESCRIPTOR);
GradleProjectStructureNode<String> result = new GradleProjectStructureNode<String>(DEPENDENCIES_NODE_DESCRIPTOR, GradleEntityType.SYNTHETIC);
moduleNode.add(result);
myModuleDependencies.put(moduleName, result);
@@ -184,7 +190,8 @@ public class GradleProjectStructureTreeModel extends DefaultTreeModel {
return;
}
}
GradleProjectStructureNode<GradleLibraryDependencyId> newNode = buildNode(id, id.getLibraryName(), GradleIcons.LIB_ICON);
GradleProjectStructureNode<GradleLibraryDependencyId> newNode
= buildNode(id, GradleEntityType.LIBRARY_DEPENDENCY, id.getLibraryName(), GradleIcons.LIB_ICON);
newNode.getDescriptor().setAttributes(attributes);
dependenciesNode.add(newNode);
nodeStructureChanged(dependenciesNode);
@@ -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<Collection<GradleProjectStructureNode<?>>> SYNC_TREE_NODE = DataKey.create("gradle.sync.tree.node");
private GradleDataKeys() {
}
}
@@ -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<T> extends DefaultMutableTreeNode implements Iterable<GradleProjectStructureNode<?>> {
private final Set<GradleProjectStructureChange> myConflictChanges = new HashSet<GradleProjectStructureChange>();
private final GradleProjectStructureNodeDescriptor<T> myDescriptor;
public GradleProjectStructureNode(@NotNull GradleProjectStructureNodeDescriptor<T> descriptor) {
private final GradleProjectStructureNodeDescriptor<T> myDescriptor;
private final GradleEntityType myType;
public GradleProjectStructureNode(@NotNull GradleProjectStructureNodeDescriptor<T> descriptor, @NotNull GradleEntityType type) {
super(descriptor);
myDescriptor = descriptor;
myType = type;
}
@NotNull
@@ -28,6 +32,11 @@ public class GradleProjectStructureNode<T> 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<T> 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;
}
@@ -46,12 +46,7 @@ public class GradleProjectStructureNodeDescriptor<T> extends PresentableNodeDesc
return myData;
}
@NotNull
public String getText() {
return myName;
}
public void setText(@NotNull String name) {
public void setName(@NotNull String name) {
myName = name;
}
@@ -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 <code>Gradle</code> 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() {
}