mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-76142: Gradle support - cannot update IDEA projects once one of build.gradle files changes
1. Refactored gradle project structure tree model to the distinct class; 2. Setup test infrastructure to allow to test project structure tree as well; 3. Corresponding project structure tree checks are added to the existing tests;
This commit is contained in:
@@ -9,6 +9,7 @@ import com.intellij.openapi.wm.ToolWindowAnchor;
|
||||
import com.intellij.openapi.wm.ex.ToolWindowManagerEx;
|
||||
import com.intellij.ui.content.impl.ContentImpl;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.gradle.diff.GradleProjectStructureHelper;
|
||||
import org.jetbrains.plugins.gradle.sync.GradleProjectStructureChangesModel;
|
||||
import org.jetbrains.plugins.gradle.sync.GradleProjectStructureChangesPanel;
|
||||
import org.jetbrains.plugins.gradle.ui.GradleIcons;
|
||||
@@ -25,10 +26,14 @@ public class GradleBootstrap extends AbstractProjectComponent {
|
||||
private static final String GRADLE_TOOL_WINDOW_ID = GradleBundle.message("gradle.name");
|
||||
|
||||
private final GradleProjectStructureChangesModel myChangesModel;
|
||||
private final GradleProjectStructureHelper myProjectStructureHelper;
|
||||
|
||||
public GradleBootstrap(@NotNull Project project, @NotNull GradleProjectStructureChangesModel changesModel) {
|
||||
public GradleBootstrap(@NotNull Project project,
|
||||
@NotNull GradleProjectStructureChangesModel changesModel,
|
||||
@NotNull GradleProjectStructureHelper projectStructureHelper) {
|
||||
super(project);
|
||||
myChangesModel = changesModel;
|
||||
myProjectStructureHelper = projectStructureHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -46,7 +51,8 @@ public class GradleBootstrap extends AbstractProjectComponent {
|
||||
ToolWindow toolWindow = manager.registerToolWindow(GRADLE_TOOL_WINDOW_ID, false, ToolWindowAnchor.RIGHT);
|
||||
toolWindow.setIcon(GradleIcons.GRADLE_ICON);
|
||||
String syncTitle = GradleBundle.message("gradle.sync.title.tab");
|
||||
final GradleProjectStructureChangesPanel projectStructureChanges = new GradleProjectStructureChangesPanel(myProject, myChangesModel);
|
||||
final GradleProjectStructureChangesPanel projectStructureChanges
|
||||
= new GradleProjectStructureChangesPanel(myProject, myChangesModel, myProjectStructureHelper);
|
||||
toolWindow.getContentManager().addContent(new ContentImpl(projectStructureChanges, syncTitle, true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.intellij.ui.ScrollPaneFactory;
|
||||
import com.intellij.util.messages.MessageBusConnection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.gradle.diff.GradleProjectStructureHelper;
|
||||
import org.jetbrains.plugins.gradle.util.GradleBundle;
|
||||
import org.jetbrains.plugins.gradle.ui.RichTextControlBuilder;
|
||||
|
||||
@@ -41,11 +42,16 @@ public abstract class GradleToolWindowPanel extends SimpleToolWindowPanel {
|
||||
/** Top-level container, managed by the card layout. */
|
||||
private final JPanel myContent = new JPanel(myLayout);
|
||||
|
||||
private final Project myProject;
|
||||
private final Project myProject;
|
||||
private final GradleProjectStructureHelper myProjectStructureHelper;
|
||||
|
||||
protected GradleToolWindowPanel(@NotNull Project project, @NotNull String place) {
|
||||
protected GradleToolWindowPanel(@NotNull Project project,
|
||||
@Nullable GradleProjectStructureHelper projectStructureHelper,
|
||||
@NotNull String place)
|
||||
{
|
||||
super(true);
|
||||
myProject = project;
|
||||
myProjectStructureHelper = projectStructureHelper;
|
||||
final ActionManager actionManager = ActionManager.getInstance();
|
||||
final ActionGroup actionGroup = (ActionGroup)actionManager.getAction(TOOL_WINDOW_TOOLBAR_ID);
|
||||
ActionToolbar actionToolbar = actionManager.createActionToolbar(place, actionGroup, true);
|
||||
@@ -91,6 +97,11 @@ public abstract class GradleToolWindowPanel extends SimpleToolWindowPanel {
|
||||
return myProject;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public GradleProjectStructureHelper getProjectStructureHelper() {
|
||||
return myProjectStructureHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return GUI control to be displayed at the current tab
|
||||
*/
|
||||
|
||||
+16
-272
@@ -1,38 +1,18 @@
|
||||
package org.jetbrains.plugins.gradle.sync;
|
||||
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.LibraryOrderEntry;
|
||||
import com.intellij.openapi.roots.ModuleRootManager;
|
||||
import com.intellij.openapi.roots.OrderEntry;
|
||||
import com.intellij.openapi.roots.RootPolicy;
|
||||
import com.intellij.ui.treeStructure.Tree;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.hash.HashMap;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.gradle.config.GradleTextAttributes;
|
||||
import org.jetbrains.plugins.gradle.config.GradleToolWindowPanel;
|
||||
import org.jetbrains.plugins.gradle.diff.*;
|
||||
import org.jetbrains.plugins.gradle.model.GradleLibraryDependency;
|
||||
import org.jetbrains.plugins.gradle.model.GradleModule;
|
||||
import org.jetbrains.plugins.gradle.model.Named;
|
||||
import org.jetbrains.plugins.gradle.ui.GradleIcons;
|
||||
import org.jetbrains.plugins.gradle.ui.GradleProjectStructureNodeDescriptor;
|
||||
import org.jetbrains.plugins.gradle.util.GradleBundle;
|
||||
import org.jetbrains.plugins.gradle.diff.GradleProjectStructureChange;
|
||||
import org.jetbrains.plugins.gradle.diff.GradleProjectStructureHelper;
|
||||
import org.jetbrains.plugins.gradle.util.GradleConstants;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import javax.swing.tree.DefaultTreeModel;
|
||||
import javax.swing.tree.MutableTreeNode;
|
||||
import javax.swing.tree.TreeNode;
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* UI control for showing difference between the gradle and intellij project structure.
|
||||
@@ -42,31 +22,15 @@ import java.util.Map;
|
||||
*/
|
||||
public class GradleProjectStructureChangesPanel extends GradleToolWindowPanel {
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* ...
|
||||
* |_module <- module's name is a key
|
||||
* |_...
|
||||
* |_dependencies <- dependencies holder node is a value
|
||||
* |_dependency1
|
||||
* |_dependency2
|
||||
* </pre>
|
||||
*/
|
||||
private final Map<String, DefaultMutableTreeNode> myModuleDependencies = new HashMap<String, DefaultMutableTreeNode>();
|
||||
private final Map<String, DefaultMutableTreeNode> myModules = new HashMap<String, DefaultMutableTreeNode>();
|
||||
private GradleProjectStructureTreeModel myTreeModel;
|
||||
private JPanel myContent;
|
||||
|
||||
private final TreeNode[] myNodeHolder = new TreeNode[1];
|
||||
private final int[] myIndexHolder = new int[1];
|
||||
|
||||
private final GradleProjectStructureChangesModel myChangesModel;
|
||||
|
||||
private DefaultTreeModel myTreeModel;
|
||||
private JPanel myContent;
|
||||
|
||||
public GradleProjectStructureChangesPanel(@NotNull Project project, @NotNull GradleProjectStructureChangesModel model) {
|
||||
super(project, GradleConstants.TOOL_WINDOW_TOOLBAR_PLACE);
|
||||
myChangesModel = model;
|
||||
myChangesModel.addListener(new GradleProjectStructureChangeListener() {
|
||||
public GradleProjectStructureChangesPanel(@NotNull Project project,
|
||||
@NotNull GradleProjectStructureChangesModel model,
|
||||
@NotNull GradleProjectStructureHelper projectStructureHelper)
|
||||
{
|
||||
super(project, projectStructureHelper, GradleConstants.TOOL_WINDOW_TOOLBAR_PLACE);
|
||||
model.addListener(new GradleProjectStructureChangeListener() {
|
||||
@Override
|
||||
public void onChanges(@NotNull final Collection<GradleProjectStructureChange> oldChanges,
|
||||
@NotNull final Collection<GradleProjectStructureChange> currentChanges)
|
||||
@@ -74,111 +38,30 @@ public class GradleProjectStructureChangesPanel extends GradleToolWindowPanel {
|
||||
UIUtil.invokeLaterIfNeeded(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateTree(currentChanges);
|
||||
processObsoleteChanges(ContainerUtil.subtract(oldChanges, currentChanges));
|
||||
|
||||
myTreeModel.update(currentChanges);
|
||||
myTreeModel.pruneObsoleteNodes(ContainerUtil.subtract(oldChanges, currentChanges));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
rebuildTree();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private DefaultTreeModel init() {
|
||||
private void init() {
|
||||
myContent = new JPanel(new GridBagLayout());
|
||||
DefaultTreeModel treeModel = new DefaultTreeModel(null);
|
||||
Tree tree = new Tree(treeModel);
|
||||
myTreeModel = new GradleProjectStructureTreeModel(getProject(), getProjectStructureHelper());
|
||||
Tree tree = new Tree(myTreeModel);
|
||||
|
||||
GridBagConstraints constraints = new GridBagConstraints();
|
||||
constraints.fill = GridBagConstraints.BOTH;
|
||||
constraints.weightx = constraints.weighty = 1;
|
||||
myContent.add(tree, constraints);
|
||||
myContent.setBackground(tree.getBackground());
|
||||
return treeModel;
|
||||
}
|
||||
|
||||
private void rebuildTree() {
|
||||
myModules.clear();
|
||||
myModuleDependencies.clear();
|
||||
|
||||
DefaultMutableTreeNode root = new DefaultMutableTreeNode(buildDescriptor(getProject()));
|
||||
final Module[] modules = ModuleManager.getInstance(getProject()).getModules();
|
||||
RootPolicy<LibraryOrderEntry> policy = new RootPolicy<LibraryOrderEntry>() {
|
||||
@Override
|
||||
public LibraryOrderEntry visitLibraryOrderEntry(LibraryOrderEntry libraryOrderEntry, LibraryOrderEntry value) {
|
||||
return libraryOrderEntry;
|
||||
}
|
||||
};
|
||||
for (Module module : modules) {
|
||||
final DefaultMutableTreeNode moduleNode = new DefaultMutableTreeNode(buildDescriptor(module));
|
||||
myModules.put(module.getName(), moduleNode); // Assuming that module names are unique.
|
||||
List<LibraryOrderEntry> libraryDependencies = new ArrayList<LibraryOrderEntry>();
|
||||
final ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
|
||||
for (OrderEntry orderEntry : moduleRootManager.getOrderEntries()) {
|
||||
final LibraryOrderEntry library = orderEntry.accept(policy, null);
|
||||
if (library != null) {
|
||||
libraryDependencies.add(library);
|
||||
}
|
||||
}
|
||||
if (!libraryDependencies.isEmpty()) {
|
||||
DefaultMutableTreeNode dependenciesNode = getDependenciesNode(module.getName());
|
||||
for (LibraryOrderEntry dependency : libraryDependencies) {
|
||||
dependenciesNode.add(new DefaultMutableTreeNode(buildDescriptor(dependency)));
|
||||
}
|
||||
}
|
||||
root.add(moduleNode);
|
||||
}
|
||||
|
||||
myTreeModel.setRoot(root);
|
||||
updateTree(myChangesModel.getChanges());
|
||||
}
|
||||
|
||||
private static GradleProjectStructureNodeDescriptor<Object> buildDescriptor(@NotNull String name, Icon icon) {
|
||||
return new GradleProjectStructureNodeDescriptor<Object>(new Object(), name, icon);
|
||||
}
|
||||
|
||||
private static GradleProjectStructureNodeDescriptor<Named> buildDescriptor(@NotNull Named entity, @NotNull Icon icon) {
|
||||
return new GradleProjectStructureNodeDescriptor<Named>(entity, entity.getName(), icon);
|
||||
}
|
||||
|
||||
private static GradleProjectStructureNodeDescriptor<Project> buildDescriptor(@NotNull Project project) {
|
||||
return new GradleProjectStructureNodeDescriptor<Project>(project, project.getName(), GradleIcons.PROJECT_ICON);
|
||||
}
|
||||
|
||||
private static GradleProjectStructureNodeDescriptor<Module> buildDescriptor(@NotNull Module module) {
|
||||
return new GradleProjectStructureNodeDescriptor<Module>(module, module.getName(), GradleIcons.MODULE_ICON);
|
||||
}
|
||||
|
||||
private static GradleProjectStructureNodeDescriptor<GradleModule> buildDescriptor(@NotNull GradleModule module) {
|
||||
return new GradleProjectStructureNodeDescriptor<GradleModule>(module, module.getName(), GradleIcons.MODULE_ICON);
|
||||
}
|
||||
|
||||
private static GradleProjectStructureNodeDescriptor<LibraryOrderEntry> buildDescriptor(@NotNull LibraryOrderEntry library) {
|
||||
return new GradleProjectStructureNodeDescriptor<LibraryOrderEntry>(library, library.getPresentableName(), GradleIcons.LIB_ICON);
|
||||
}
|
||||
|
||||
private DefaultMutableTreeNode getDependenciesNode(@NotNull String moduleName) {
|
||||
final DefaultMutableTreeNode cached = myModuleDependencies.get(moduleName);
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
}
|
||||
DefaultMutableTreeNode moduleNode = myModules.get(moduleName);
|
||||
if (moduleNode == null) {
|
||||
moduleNode = new DefaultMutableTreeNode(buildDescriptor(moduleName, GradleIcons.MODULE_ICON));
|
||||
myModules.put(moduleName, moduleNode);
|
||||
}
|
||||
|
||||
DefaultMutableTreeNode result = new DefaultMutableTreeNode(GradleBundle.message("gradle.project.structure.tree.node.dependencies"));
|
||||
moduleNode.add(result);
|
||||
myModuleDependencies.put(moduleName, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected JComponent buildContent() {
|
||||
myTreeModel = init();
|
||||
init();
|
||||
return myContent;
|
||||
}
|
||||
|
||||
@@ -188,143 +71,4 @@ public class GradleProjectStructureChangesPanel extends GradleToolWindowPanel {
|
||||
int i = 1;
|
||||
}
|
||||
|
||||
private void updateTree(@NotNull Collection<GradleProjectStructureChange> changes) {
|
||||
for (GradleProjectStructureChange change : changes) {
|
||||
change.invite(new GradleProjectStructureChangeVisitor() {
|
||||
@Override
|
||||
public void visit(@NotNull GradleRenameChange change) {
|
||||
// TODO den implement
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(@NotNull GradleProjectStructureChange change) {
|
||||
// TODO den implement
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(@NotNull GradleModulePresenceChange change) {
|
||||
// TODO den implement
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(@NotNull GradleLibraryDependencyPresenceChange change) {
|
||||
String moduleName;
|
||||
GradleProjectStructureNodeDescriptor<?> descriptor;
|
||||
final GradleLibraryDependency gradleEntity = change.getGradleEntity();
|
||||
final LibraryOrderEntry intellijEntity = change.getIntellijEntity();
|
||||
final Object missingEntity;
|
||||
if (gradleEntity == null && intellijEntity == null) {
|
||||
// Never expect to be here.
|
||||
assert false;
|
||||
}
|
||||
|
||||
if (gradleEntity == null) {
|
||||
// Particular library dependency is added at the intellij side.
|
||||
moduleName = intellijEntity.getOwnerModule().getName();
|
||||
descriptor = buildDescriptor(intellijEntity);
|
||||
descriptor.setAttributes(GradleTextAttributes.INTELLIJ_LOCAL_CHANGE);
|
||||
missingEntity = intellijEntity;
|
||||
}
|
||||
else {
|
||||
// Particular library dependency is added at the gradle side.
|
||||
moduleName = gradleEntity.getOwnerModule().getName();
|
||||
descriptor = buildDescriptor(gradleEntity, GradleIcons.LIB_ICON);
|
||||
descriptor.setAttributes(GradleTextAttributes.GRADLE_LOCAL_CHANGE);
|
||||
missingEntity = gradleEntity;
|
||||
}
|
||||
final DefaultMutableTreeNode dependenciesNode = getDependenciesNode(moduleName);
|
||||
for (int i = 0, max = dependenciesNode.getChildCount(); i < max; i++) {
|
||||
final DefaultMutableTreeNode child = (DefaultMutableTreeNode)dependenciesNode.getChildAt(i);
|
||||
GradleProjectStructureNodeDescriptor<?> d = (GradleProjectStructureNodeDescriptor<?>)child.getUserObject();
|
||||
if (missingEntity.equals(d.getElement())) {
|
||||
d.setAttributes(descriptor.getAttributes());
|
||||
return;
|
||||
}
|
||||
}
|
||||
DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(descriptor);
|
||||
dependenciesNode.add(newNode);
|
||||
myTreeModel.nodeStructureChanged(dependenciesNode);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the tree state considering that the given changes are obsolete.
|
||||
* <p/>
|
||||
* Example:
|
||||
* <pre>
|
||||
* <ol>
|
||||
* <li>There is a particular intellij-local library (change from the gradle project structure);</li>
|
||||
* <li>Corresponding node is shown at the current UI;</li>
|
||||
* <li>The library is removed, i.e. corresponding change has become obsolete;</li>
|
||||
* <li>This method is notified within the obsolete change and is expected to remove the corresponding node;</li>
|
||||
* </ol>
|
||||
* </pre>
|
||||
*/
|
||||
private void processObsoleteChanges(Collection<GradleProjectStructureChange> changes) {
|
||||
for (GradleProjectStructureChange change : changes) {
|
||||
change.invite(new GradleProjectStructureChangeVisitor() {
|
||||
@Override
|
||||
public void visit(@NotNull GradleRenameChange change) {
|
||||
// TODO den implement
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(@NotNull GradleProjectStructureChange change) {
|
||||
// TODO den implement
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(@NotNull GradleModulePresenceChange change) {
|
||||
// TODO den implement
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(@NotNull GradleLibraryDependencyPresenceChange change) {
|
||||
// We need to remove the corresponding node then.
|
||||
String moduleName;
|
||||
Object library;
|
||||
final GradleLibraryDependency gradleEntity = change.getGradleEntity();
|
||||
final LibraryOrderEntry intellijEntity = change.getIntellijEntity();
|
||||
assert gradleEntity != null || intellijEntity != null;
|
||||
if (gradleEntity == null) {
|
||||
moduleName = intellijEntity.getOwnerModule().getName();
|
||||
library = intellijEntity;
|
||||
}
|
||||
else {
|
||||
moduleName = gradleEntity.getOwnerModule().getName();
|
||||
library = gradleEntity;
|
||||
}
|
||||
final DefaultMutableTreeNode holder = myModuleDependencies.get(moduleName);
|
||||
if (holder == null) {
|
||||
return;
|
||||
}
|
||||
for (DefaultMutableTreeNode node = holder.getFirstLeaf(); node != null; node = node.getNextSibling()) {
|
||||
GradleProjectStructureNodeDescriptor<?> descriptor = (GradleProjectStructureNodeDescriptor<?>)node.getUserObject();
|
||||
if (descriptor.getElement().equals(library)) {
|
||||
removeNode(node);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void removeNode(@NotNull TreeNode node) {
|
||||
final MutableTreeNode parent = (MutableTreeNode)node.getParent();
|
||||
if (parent == null) {
|
||||
return;
|
||||
}
|
||||
int i = parent.getIndex(node);
|
||||
if (i <= 0) {
|
||||
assert false : node;
|
||||
return;
|
||||
}
|
||||
parent.remove(i);
|
||||
myIndexHolder[0] = i;
|
||||
myNodeHolder[0] = node;
|
||||
myTreeModel.nodesWereRemoved(parent, myIndexHolder, myNodeHolder);
|
||||
}
|
||||
}
|
||||
|
||||
+295
@@ -0,0 +1,295 @@
|
||||
package org.jetbrains.plugins.gradle.sync;
|
||||
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.LibraryOrderEntry;
|
||||
import com.intellij.openapi.roots.OrderEntry;
|
||||
import com.intellij.openapi.roots.RootPolicy;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.containers.hash.HashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.gradle.config.GradleTextAttributes;
|
||||
import org.jetbrains.plugins.gradle.diff.*;
|
||||
import org.jetbrains.plugins.gradle.model.GradleLibraryDependency;
|
||||
import org.jetbrains.plugins.gradle.model.Named;
|
||||
import org.jetbrains.plugins.gradle.ui.GradleIcons;
|
||||
import org.jetbrains.plugins.gradle.ui.GradleProjectStructureNodeDescriptor;
|
||||
import org.jetbrains.plugins.gradle.util.GradleBundle;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import javax.swing.tree.DefaultTreeModel;
|
||||
import javax.swing.tree.MutableTreeNode;
|
||||
import javax.swing.tree.TreeNode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Model for the target project structure tree used by the gradle integration.
|
||||
* <p/>
|
||||
* Not thread-safe.
|
||||
*
|
||||
* @author Denis Zhdanov
|
||||
* @since 1/30/12 4:20 PM
|
||||
*/
|
||||
public class GradleProjectStructureTreeModel extends DefaultTreeModel {
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* ...
|
||||
* |_module <- module's name is a key
|
||||
* |_...
|
||||
* |_dependencies <- dependencies holder node is a value
|
||||
* |_dependency1
|
||||
* |_dependency2
|
||||
* </pre>
|
||||
*/
|
||||
private final Map<String, DefaultMutableTreeNode> myModuleDependencies = new HashMap<String, DefaultMutableTreeNode>();
|
||||
private final Map<String, DefaultMutableTreeNode> myModules = new HashMap<String, DefaultMutableTreeNode>();
|
||||
|
||||
private final TreeNode[] myNodeHolder = new TreeNode[1];
|
||||
private final int[] myIndexHolder = new int[1];
|
||||
|
||||
private final Project myProject;
|
||||
private final GradleProjectStructureHelper myProjectStructureHelper;
|
||||
|
||||
public GradleProjectStructureTreeModel(@NotNull Project project, @NotNull GradleProjectStructureHelper projectStructureHelper) {
|
||||
super(null);
|
||||
myProject = project;
|
||||
myProjectStructureHelper = projectStructureHelper;
|
||||
rebuild();
|
||||
}
|
||||
|
||||
public void rebuild() {
|
||||
myModuleDependencies.clear();
|
||||
myModules.clear();
|
||||
|
||||
DefaultMutableTreeNode root = new DefaultMutableTreeNode(buildDescriptor(getProject()));
|
||||
final Collection<Module> modules = myProjectStructureHelper.getModules(getProject());
|
||||
RootPolicy<LibraryOrderEntry> policy = new RootPolicy<LibraryOrderEntry>() {
|
||||
@Override
|
||||
public LibraryOrderEntry visitLibraryOrderEntry(LibraryOrderEntry libraryOrderEntry, LibraryOrderEntry value) {
|
||||
return libraryOrderEntry;
|
||||
}
|
||||
};
|
||||
for (Module module : modules) {
|
||||
final DefaultMutableTreeNode moduleNode = new DefaultMutableTreeNode(buildDescriptor(module));
|
||||
myModules.put(module.getName(), moduleNode); // Assuming that module names are unique.
|
||||
List<LibraryOrderEntry> libraryDependencies = new ArrayList<LibraryOrderEntry>();
|
||||
for (OrderEntry orderEntry : myProjectStructureHelper.getOrderEntries(module)) {
|
||||
final LibraryOrderEntry libraryDependency = orderEntry.accept(policy, null);
|
||||
if (libraryDependency != null && !StringUtil.isEmpty(libraryDependency.getLibraryName())) {
|
||||
libraryDependencies.add(libraryDependency);
|
||||
}
|
||||
}
|
||||
if (!libraryDependencies.isEmpty()) {
|
||||
DefaultMutableTreeNode dependenciesNode = getDependenciesNode(module.getName());
|
||||
for (LibraryOrderEntry dependency : libraryDependencies) {
|
||||
dependenciesNode.add(new DefaultMutableTreeNode(buildDescriptor(dependency)));
|
||||
}
|
||||
moduleNode.add(dependenciesNode);
|
||||
}
|
||||
root.add(moduleNode);
|
||||
}
|
||||
|
||||
setRoot(root);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Project getProject() {
|
||||
return myProject;
|
||||
}
|
||||
|
||||
private static GradleProjectStructureNodeDescriptor<Object> buildDescriptor(@NotNull String name, @Nullable Icon icon) {
|
||||
return new GradleProjectStructureNodeDescriptor<Object>(new Object(), name, icon);
|
||||
}
|
||||
|
||||
private static GradleProjectStructureNodeDescriptor<Named> buildDescriptor(@NotNull Named entity, @NotNull Icon icon) {
|
||||
return new GradleProjectStructureNodeDescriptor<Named>(entity, entity.getName(), icon);
|
||||
}
|
||||
|
||||
private static GradleProjectStructureNodeDescriptor<Project> buildDescriptor(@NotNull Project project) {
|
||||
return new GradleProjectStructureNodeDescriptor<Project>(project, project.getName(), GradleIcons.PROJECT_ICON);
|
||||
}
|
||||
|
||||
private static GradleProjectStructureNodeDescriptor<Module> buildDescriptor(@NotNull Module module) {
|
||||
return new GradleProjectStructureNodeDescriptor<Module>(module, module.getName(), GradleIcons.MODULE_ICON);
|
||||
}
|
||||
|
||||
private static GradleProjectStructureNodeDescriptor<LibraryOrderEntry> buildDescriptor(@NotNull LibraryOrderEntry library) {
|
||||
final String name = library.getLibraryName();
|
||||
assert name != null;
|
||||
return new GradleProjectStructureNodeDescriptor<LibraryOrderEntry>(library, name, GradleIcons.LIB_ICON);
|
||||
}
|
||||
|
||||
private DefaultMutableTreeNode getDependenciesNode(@NotNull String moduleName) {
|
||||
final DefaultMutableTreeNode cached = myModuleDependencies.get(moduleName);
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
}
|
||||
DefaultMutableTreeNode moduleNode = myModules.get(moduleName);
|
||||
if (moduleNode == null) {
|
||||
moduleNode = new DefaultMutableTreeNode(buildDescriptor(moduleName, GradleIcons.MODULE_ICON));
|
||||
myModules.put(moduleName, moduleNode);
|
||||
}
|
||||
|
||||
DefaultMutableTreeNode result = new DefaultMutableTreeNode(buildDescriptor(
|
||||
GradleBundle.message("gradle.project.structure.tree.node.dependencies"),
|
||||
null
|
||||
));
|
||||
moduleNode.add(result);
|
||||
myModuleDependencies.put(moduleName, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks current model to update its state in accordance with the given changes.
|
||||
*
|
||||
* @param changes collections that contains all changes between the current gradle and intellij project structures
|
||||
*/
|
||||
public void update(@NotNull Collection<GradleProjectStructureChange> changes) {
|
||||
for (GradleProjectStructureChange change : changes) {
|
||||
change.invite(new GradleProjectStructureChangeVisitor() {
|
||||
@Override
|
||||
public void visit(@NotNull GradleRenameChange change) {
|
||||
// TODO den implement
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(@NotNull GradleProjectStructureChange change) {
|
||||
// TODO den implement
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(@NotNull GradleModulePresenceChange change) {
|
||||
// TODO den implement
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(@NotNull GradleLibraryDependencyPresenceChange change) {
|
||||
String moduleName;
|
||||
GradleProjectStructureNodeDescriptor<?> descriptor;
|
||||
final GradleLibraryDependency gradleEntity = change.getGradleEntity();
|
||||
final LibraryOrderEntry intellijEntity = change.getIntellijEntity();
|
||||
final Object missingEntity;
|
||||
if (gradleEntity == null && intellijEntity == null) {
|
||||
// Never expect to be here.
|
||||
assert false;
|
||||
}
|
||||
|
||||
if (gradleEntity == null) {
|
||||
// Particular library dependency is added at the intellij side.
|
||||
moduleName = intellijEntity.getOwnerModule().getName();
|
||||
if (intellijEntity.getLibraryName() == null) {
|
||||
return;
|
||||
}
|
||||
descriptor = buildDescriptor(intellijEntity);
|
||||
descriptor.setAttributes(GradleTextAttributes.INTELLIJ_LOCAL_CHANGE);
|
||||
missingEntity = intellijEntity;
|
||||
}
|
||||
else {
|
||||
// Particular library dependency is added at the gradle side.
|
||||
moduleName = gradleEntity.getOwnerModule().getName();
|
||||
descriptor = buildDescriptor(gradleEntity, GradleIcons.LIB_ICON);
|
||||
descriptor.setAttributes(GradleTextAttributes.GRADLE_LOCAL_CHANGE);
|
||||
missingEntity = gradleEntity;
|
||||
}
|
||||
final DefaultMutableTreeNode dependenciesNode = getDependenciesNode(moduleName);
|
||||
for (int i = 0, max = dependenciesNode.getChildCount(); i < max; i++) {
|
||||
final DefaultMutableTreeNode child = (DefaultMutableTreeNode)dependenciesNode.getChildAt(i);
|
||||
GradleProjectStructureNodeDescriptor<?> d = (GradleProjectStructureNodeDescriptor<?>)child.getUserObject();
|
||||
if (missingEntity.equals(d.getElement())) {
|
||||
d.setAttributes(descriptor.getAttributes());
|
||||
return;
|
||||
}
|
||||
}
|
||||
DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(descriptor);
|
||||
dependenciesNode.add(newNode);
|
||||
nodeStructureChanged(dependenciesNode);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks current model to remove all obsolete nodes for the considering that the given changes are obsolete.
|
||||
* <p/>
|
||||
* Example:
|
||||
* <pre>
|
||||
* <ol>
|
||||
* <li>There is a particular intellij-local library (change from the gradle project structure);</li>
|
||||
* <li>Corresponding node is shown at the current UI;</li>
|
||||
* <li>The library is removed, i.e. corresponding change has become obsolete;</li>
|
||||
* <li>This method is notified within the obsolete change and is expected to remove the corresponding node;</li>
|
||||
* </ol>
|
||||
* </pre>
|
||||
*/
|
||||
public void pruneObsoleteNodes(Collection<GradleProjectStructureChange> changes) {
|
||||
for (GradleProjectStructureChange change : changes) {
|
||||
change.invite(new GradleProjectStructureChangeVisitor() {
|
||||
@Override
|
||||
public void visit(@NotNull GradleRenameChange change) {
|
||||
// TODO den implement
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(@NotNull GradleProjectStructureChange change) {
|
||||
// TODO den implement
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(@NotNull GradleModulePresenceChange change) {
|
||||
// TODO den implement
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(@NotNull GradleLibraryDependencyPresenceChange change) {
|
||||
// We need to remove the corresponding node then.
|
||||
String moduleName;
|
||||
Object library;
|
||||
final GradleLibraryDependency gradleEntity = change.getGradleEntity();
|
||||
final LibraryOrderEntry intellijEntity = change.getIntellijEntity();
|
||||
assert gradleEntity != null || intellijEntity != null;
|
||||
if (gradleEntity == null) {
|
||||
moduleName = intellijEntity.getOwnerModule().getName();
|
||||
library = intellijEntity;
|
||||
}
|
||||
else {
|
||||
moduleName = gradleEntity.getOwnerModule().getName();
|
||||
library = gradleEntity;
|
||||
}
|
||||
final DefaultMutableTreeNode holder = myModuleDependencies.get(moduleName);
|
||||
if (holder == null) {
|
||||
return;
|
||||
}
|
||||
for (DefaultMutableTreeNode node = holder.getFirstLeaf(); node != null; node = node.getNextSibling()) {
|
||||
GradleProjectStructureNodeDescriptor<?> descriptor = (GradleProjectStructureNodeDescriptor<?>)node.getUserObject();
|
||||
if (descriptor.getElement().equals(library)) {
|
||||
removeNode(node);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void removeNode(@NotNull TreeNode node) {
|
||||
final MutableTreeNode parent = (MutableTreeNode)node.getParent();
|
||||
if (parent == null) {
|
||||
return;
|
||||
}
|
||||
int i = parent.getIndex(node);
|
||||
if (i <= 0) {
|
||||
assert false : node;
|
||||
return;
|
||||
}
|
||||
parent.remove(i);
|
||||
myIndexHolder[0] = i;
|
||||
myNodeHolder[0] = node;
|
||||
nodesWereRemoved(parent, myIndexHolder, myNodeHolder);
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -4,6 +4,7 @@ import com.intellij.ide.projectView.PresentationData;
|
||||
import com.intellij.ide.util.treeView.PresentableNodeDescriptor;
|
||||
import com.intellij.openapi.editor.colors.TextAttributesKey;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.gradle.config.GradleTextAttributes;
|
||||
|
||||
import javax.swing.*;
|
||||
@@ -24,7 +25,7 @@ public class GradleProjectStructureNodeDescriptor<T> extends PresentableNodeDesc
|
||||
private final T myData;
|
||||
|
||||
@SuppressWarnings("NullableProblems")
|
||||
public GradleProjectStructureNodeDescriptor(@NotNull T data, @NotNull String text, @NotNull Icon icon) {
|
||||
public GradleProjectStructureNodeDescriptor(@NotNull T data, @NotNull String text, @Nullable Icon icon) {
|
||||
super(null, null);
|
||||
myData = data;
|
||||
myOpenIcon = myClosedIcon = icon;
|
||||
|
||||
+69
-25
@@ -1,76 +1,120 @@
|
||||
package org.jetbrains.plugins.gradle.sync;
|
||||
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.Application
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.ex.ApplicationInfoEx
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.util.containers.ContainerUtil
|
||||
import org.jetbrains.plugins.gradle.testutil.ChangeBuilder
|
||||
import org.jetbrains.plugins.gradle.testutil.GradleProjectBuilder
|
||||
import org.jetbrains.plugins.gradle.testutil.IntellijProjectBuilder
|
||||
import org.jetbrains.plugins.gradle.testutil.ProjectStructureChecker
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.picocontainer.defaults.DefaultPicoContainer
|
||||
import org.jetbrains.plugins.gradle.diff.*
|
||||
import static org.junit.Assert.assertEquals
|
||||
import com.intellij.testFramework.SkipInHeadlessEnvironment
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 01/25/2012
|
||||
*/
|
||||
@SkipInHeadlessEnvironment
|
||||
public class GradleProjectStructureChangesModelTest {
|
||||
|
||||
private GradleProjectStructureChangesModel myModel;
|
||||
def gradle;
|
||||
def intellij;
|
||||
def changes;
|
||||
GradleProjectStructureChangesModel changesModel
|
||||
GradleProjectStructureTreeModel treeModel
|
||||
def gradle
|
||||
def intellij
|
||||
def changes
|
||||
def treeChecker
|
||||
def container
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
gradle = new GradleProjectBuilder()
|
||||
intellij = new IntellijProjectBuilder()
|
||||
changes = new ChangeBuilder()
|
||||
def container = new DefaultPicoContainer()
|
||||
treeChecker = new ProjectStructureChecker()
|
||||
container = new DefaultPicoContainer()
|
||||
container.registerComponentInstance(Project, intellij.project)
|
||||
container.registerComponentInstance(GradleProjectStructureHelper, intellij.projectStructureHelper as GradleProjectStructureHelper)
|
||||
container.registerComponentImplementation(GradleProjectStructureChangesModel)
|
||||
container.registerComponentImplementation(GradleStructureChangesCalculator, GradleProjectStructureChangesCalculator)
|
||||
container.registerComponentImplementation(GradleModuleStructureChangesCalculator)
|
||||
container.registerComponentImplementation(GradleLibraryDependencyStructureChangesCalculator)
|
||||
|
||||
myModel = container.getComponentInstance(GradleProjectStructureChangesModel.class) as GradleProjectStructureChangesModel
|
||||
container.registerComponentImplementation(GradleProjectStructureTreeModel)
|
||||
|
||||
changesModel = container.getComponentInstance(GradleProjectStructureChangesModel) as GradleProjectStructureChangesModel
|
||||
def applicationInfo = [getSmallIconUrl: {"/nodes/ideaProject.png"}] as ApplicationInfoEx
|
||||
ApplicationManager.setApplication([getComponent: { applicationInfo } ] as Application, [dispose: { }] as Disposable)
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mergeGradleLocalToIntellij() {
|
||||
gradle {
|
||||
module {
|
||||
dependencies {
|
||||
lib(name: "lib1")
|
||||
lib(name: "lib2")
|
||||
} } }
|
||||
init(
|
||||
gradle {
|
||||
module {
|
||||
dependencies {
|
||||
lib(name: "lib1")
|
||||
lib(name: "lib2")
|
||||
} } },
|
||||
|
||||
intellij {
|
||||
module {
|
||||
dependencies {
|
||||
lib(name: "lib1")
|
||||
} } }
|
||||
)
|
||||
|
||||
intellij {
|
||||
module {
|
||||
dependencies {
|
||||
lib(name: "lib1")
|
||||
} } }
|
||||
|
||||
myModel.update(gradle.project)
|
||||
checkChanges {
|
||||
presence {
|
||||
lib(gradle: gradle.modules.dependencies.flatten().findAll { it.name == "lib2" })
|
||||
}
|
||||
}
|
||||
} }
|
||||
checkTree {
|
||||
project {
|
||||
module("xxx") {
|
||||
dependencies {
|
||||
lib1()
|
||||
lib2('gradle')
|
||||
} } } }
|
||||
|
||||
gradle {
|
||||
module {
|
||||
dependencies {
|
||||
lib(name: "lib1")
|
||||
} } }
|
||||
myModel.update(gradle.project)
|
||||
assertEquals([].toSet(), myModel.changes)
|
||||
changesModel.update(gradle.project)
|
||||
assertEquals([].toSet(), changesModel.changes)
|
||||
checkTree {
|
||||
project {
|
||||
module {
|
||||
dependencies {
|
||||
lib1()
|
||||
} } } }
|
||||
}
|
||||
|
||||
private def init(gradleProjectInit, intellijProjectInit) {
|
||||
treeModel = container.getComponentInstance(GradleProjectStructureTreeModel) as GradleProjectStructureTreeModel
|
||||
changesModel.addListener({ old, current ->
|
||||
treeModel.update(current)
|
||||
treeModel.pruneObsoleteNodes(ContainerUtil.<GradleProjectStructureChange>subtract(old, current));
|
||||
} as GradleProjectStructureChangeListener)
|
||||
changesModel.update(gradle.project)
|
||||
}
|
||||
|
||||
private def checkChanges(c) {
|
||||
c.delegate = changes
|
||||
assertEquals(c(), myModel.changes)
|
||||
assertEquals(c(), changesModel.changes)
|
||||
}
|
||||
|
||||
private def checkTree(c) {
|
||||
def nodeBuilder = new NodeBuilder()
|
||||
c.delegate = nodeBuilder
|
||||
def expected = c()
|
||||
treeChecker.check(expected, treeModel.root)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ class IntellijProjectBuilder extends AbstractProjectBuilder {
|
||||
|
||||
@Override
|
||||
protected createLibrary(String name, Map paths) {
|
||||
[ getName: { name } ] as Library
|
||||
[ getName: { name }, getPresentableName: { name } ] as Library
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
package org.jetbrains.plugins.gradle.testutil
|
||||
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.project.Project
|
||||
import javax.swing.tree.DefaultMutableTreeNode
|
||||
import org.jetbrains.plugins.gradle.config.GradleTextAttributes
|
||||
import org.jetbrains.plugins.gradle.ui.GradleProjectStructureNodeDescriptor
|
||||
import org.junit.Assert
|
||||
import static junit.framework.Assert.assertEquals
|
||||
import static junit.framework.Assert.fail
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 1/30/12 6:04 PM
|
||||
*/
|
||||
class ProjectStructureChecker {
|
||||
|
||||
static def BUILT_IN = [
|
||||
"project": Project,
|
||||
"module" : Module
|
||||
]
|
||||
|
||||
static def COLORS = [
|
||||
'gradle' : GradleTextAttributes.GRADLE_LOCAL_CHANGE,
|
||||
'intellij' : GradleTextAttributes.INTELLIJ_LOCAL_CHANGE,
|
||||
'conflict' : GradleTextAttributes.GRADLE_CHANGE_CONFLICT
|
||||
]
|
||||
|
||||
def check(Node expected, DefaultMutableTreeNode actual) {
|
||||
GradleProjectStructureNodeDescriptor descriptor = actual.userObject as GradleProjectStructureNodeDescriptor
|
||||
checkName(expected, descriptor)
|
||||
checkMarkup(expected, descriptor)
|
||||
int childIndex = 0
|
||||
for (it in expected.children().findAll { it instanceof Collection}) {
|
||||
check it as Node, actual.getChildAt(childIndex++) as DefaultMutableTreeNode
|
||||
}
|
||||
for (it in expected.children().findAll { it instanceof Node}) {
|
||||
check it as Node, actual.getChildAt(childIndex++) as DefaultMutableTreeNode
|
||||
}
|
||||
if (childIndex < actual.childCount) {
|
||||
fail("Unexpected nodes detected: ${(childIndex..<actual.childCount).collect { actual.getChildAt(it) } join '-'}")
|
||||
}
|
||||
}
|
||||
|
||||
private void checkName(Node expected, GradleProjectStructureNodeDescriptor actual) {
|
||||
if (expected.name() == actual.toString()) {
|
||||
return
|
||||
}
|
||||
def clazz = BUILT_IN[expected.name()]
|
||||
if (clazz == null || !clazz.isAssignableFrom(actual.element.class)) {
|
||||
Assert.fail(
|
||||
"Failed node name check. Expected to find name '${expected.name()}'" + (clazz ? " or user object of type ${clazz.simpleName}" : "")
|
||||
+ " but got: name='$actual', user object=${actual.element} "
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def checkMarkup(Node node, GradleProjectStructureNodeDescriptor descriptor) {
|
||||
def expected = COLORS[node.children().find {it instanceof CharSequence}]?: GradleTextAttributes.GRADLE_NO_CHANGE
|
||||
assertEquals(expected, descriptor.attributes)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user