IDEA-76142: Gradle support - cannot update IDEA projects once one of build.gradle files changes

Refactored huge visitors to tiny visitors and separate methods with business logic
This commit is contained in:
Denis.Zhdanov
2012-02-16 08:00:56 +04:00
parent 7662993267
commit 30874db6ec
2 changed files with 237 additions and 201 deletions
@@ -4,11 +4,10 @@ import com.intellij.openapi.module.Module;
import com.intellij.openapi.roots.LibraryOrderEntry;
import com.intellij.openapi.roots.ModuleOrderEntry;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.util.Ref;
import com.intellij.util.containers.Stack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.gradle.config.GradleTextAttributes;
import org.jetbrains.plugins.gradle.model.*;
import org.jetbrains.plugins.gradle.model.GradleEntityType;
import org.jetbrains.plugins.gradle.model.gradle.*;
import org.jetbrains.plugins.gradle.model.id.GradleEntityId;
import org.jetbrains.plugins.gradle.model.id.GradleEntityIdMapper;
@@ -38,6 +37,8 @@ import java.util.*;
* </li>
* </ul>
* </pre>
* <p/>
* Thread-safe.
*
* @author Denis Zhdanov
* @since 2/10/12 11:51 AM
@@ -86,72 +87,14 @@ public class GradleLocalNodeImportHelper {
*/
@NotNull
public List<GradleEntity> deriveEntitiesToImport(@NotNull Iterable<GradleProjectStructureNode<?>> nodes) {
EntitiesToImport toImport = new EntitiesToImport();
Context context = new Context();
for (GradleProjectStructureNode<?> node : nodes) {
collectEntitiesToImport(node, toImport);
collectEntitiesToImport(node, context);
}
return toImport.getAll();
return context.getAll();
}
private void collectEntitiesToImport(@NotNull GradleProjectStructureNode<?> node, @NotNull final EntitiesToImport toImport) {
final Ref<Boolean> recursive = new Ref<Boolean>(false);
final GradleEntityVisitor visitor = new GradleEntityVisitorAdapter() {
@Override
public void visit(@NotNull GradleModule module) {
final Module intellijModule = myProjectStructureHelper.findIntellijModule(module);
if (intellijModule != null) {
// Already imported
return;
}
toImport.modules.add(module);
if (!recursive.get()) {
return;
}
for (GradleDependency dependency : module.getDependencies()) {
dependency.invite(this);
}
}
@Override
public void visit(@NotNull GradleModuleDependency dependency) {
final ModuleOrderEntry intellijModuleDependency = myProjectStructureHelper.findIntellijModuleDependency(dependency);
if (intellijModuleDependency != null) {
// Already imported.
return;
}
toImport.dependencies.add(dependency);
final GradleModule gradleModule = dependency.getTarget();
final Module intellijModule = myProjectStructureHelper.findIntellijModule(gradleModule);
if (intellijModule != null) {
return;
}
boolean r = recursive.get();
recursive.set(true);
try {
gradleModule.invite(this);
}
finally {
recursive.set(r);
}
}
@Override
public void visit(@NotNull GradleLibraryDependency dependency) {
final LibraryOrderEntry intellijDependency
= myProjectStructureHelper.findIntellijLibraryDependency(dependency.getOwnerModule().getName(), dependency.getName());
if (intellijDependency != null) {
// Already imported.
return;
}
toImport.dependencies.add(dependency);
final GradleLibrary gradleLibrary = dependency.getTarget();
final Library intellijLibrary = myProjectStructureHelper.findIntellijLibrary(gradleLibrary);
if (intellijLibrary == null) {
toImport.libraries.add(gradleLibrary);
}
}
};
private void collectEntitiesToImport(@NotNull GradleProjectStructureNode<?> node, @NotNull Context context) {
// Collect up.
for (GradleProjectStructureNode<?> n = node.getParent(); n != null; n = n.getParent()) {
final GradleProjectStructureNodeDescriptor<?> descriptor = n.getDescriptor();
@@ -165,7 +108,7 @@ public class GradleLocalNodeImportHelper {
if (id instanceof GradleEntityId) {
final Object entity = myIdMapper.mapIdToEntity((GradleEntityId)id);
if (entity instanceof GradleEntity) {
((GradleEntity)entity).invite(visitor);
((GradleEntity)entity).invite(context.visitor);
}
}
}
@@ -180,15 +123,67 @@ public class GradleLocalNodeImportHelper {
}
}
recursive.set(true);
context.recursive = true;
while (!toProcess.isEmpty()) {
final GradleEntity entity = toProcess.pop();
entity.invite(visitor);
entity.invite(context.visitor);
}
}
private void collectModuleEntities(@NotNull GradleModule module, @NotNull Context context) {
final Module intellijModule = myProjectStructureHelper.findIntellijModule(module);
if (intellijModule != null) {
// Already imported
return;
}
context.modules.add(module);
if (!context.recursive) {
return;
}
for (GradleDependency dependency : module.getDependencies()) {
dependency.invite(context.visitor);
}
}
private void collectModuleDependencyEntities(@NotNull GradleModuleDependency dependency, @NotNull Context context) {
final ModuleOrderEntry intellijModuleDependency = myProjectStructureHelper.findIntellijModuleDependency(dependency);
if (intellijModuleDependency != null) {
// Already imported.
return;
}
context.dependencies.add(dependency);
final GradleModule gradleModule = dependency.getTarget();
final Module intellijModule = myProjectStructureHelper.findIntellijModule(gradleModule);
if (intellijModule != null) {
return;
}
boolean r = context.recursive;
context.recursive = true;
try {
gradleModule.invite(context.visitor);
}
finally {
context.recursive = r;
}
}
private void collectLibraryDependencyEntities(@NotNull GradleLibraryDependency dependency, @NotNull Context context) {
final LibraryOrderEntry intellijDependency
= myProjectStructureHelper.findIntellijLibraryDependency(dependency.getOwnerModule().getName(), dependency.getName());
if (intellijDependency != null) {
// Already imported.
return;
}
context.dependencies.add(dependency);
final GradleLibrary gradleLibrary = dependency.getTarget();
final Library intellijLibrary = myProjectStructureHelper.findIntellijLibrary(gradleLibrary);
if (intellijLibrary == null) {
context.libraries.add(gradleLibrary);
}
}
/**
* Imports given 'gradle-local' entities to the current intellij projcet.
* Imports given 'gradle-local' entities to the current intellij project.
*
* @param entities target 'gradle-local' entities to import
*/
@@ -239,11 +234,14 @@ public class GradleLocalNodeImportHelper {
}
}
private static class EntitiesToImport {
private class Context {
public final Set<GradleModule> modules = new HashSet<GradleModule>();
public final Set<GradleLibrary> libraries = new HashSet<GradleLibrary>();
public final Set<GradleDependency> dependencies = new HashSet<GradleDependency>();
public final CollectingVisitor visitor = new CollectingVisitor(this);
public boolean recursive;
@NotNull
public List<GradleEntity> getAll() {
@@ -254,4 +252,22 @@ public class GradleLocalNodeImportHelper {
return result;
}
}
private class CollectingVisitor implements GradleEntityVisitor {
@NotNull private final Context myContext;
CollectingVisitor(@NotNull Context context) {
myContext = context;
}
@Override public void visit(@NotNull GradleProject project) { }
@Override public void visit(@NotNull GradleModule module) { collectModuleEntities(module, myContext); }
@Override
public void visit(@NotNull GradleContentRoot contentRoot) {
// TODO den implement
}
@Override public void visit(@NotNull GradleLibrary library) { /* Assuming that a library may be imported only as a dependency */ }
@Override public void visit(@NotNull GradleModuleDependency dependency) { collectModuleDependencyEntities(dependency, myContext); }
@Override public void visit(@NotNull GradleLibraryDependency dependency) { collectLibraryDependencyEntities(dependency, myContext); }
}
}
@@ -51,9 +51,11 @@ public class GradleProjectStructureTreeModel extends DefaultTreeModel {
private final Map<String, GradleProjectStructureNode<GradleModuleId>> myModules
= new HashMap<String, GradleProjectStructureNode<GradleModuleId>>();
private final TreeNode[] myNodeHolder = new TreeNode[1];
private final int[] myIndexHolder = new int[1];
private final NodeListener myNodeListener = new NodeListener();
private final TreeNode[] myNodeHolder = new TreeNode[1];
private final int[] myIndexHolder = new int[1];
private final NodeListener myNodeListener = new NodeListener();
private final ObsoleteChangesDispatcher myObsoleteChangesDispatcher = new ObsoleteChangesDispatcher();
private final NewChangesDispatcher myNewChangesDispatcher = new NewChangesDispatcher();
@NotNull private final Project myProject;
@NotNull private final PlatformFacade myPlatformFacade;
@@ -169,62 +171,7 @@ public class GradleProjectStructureTreeModel extends DefaultTreeModel {
*/
public void update(@NotNull Collection<GradleProjectStructureChange> changes) {
for (GradleProjectStructureChange change : changes) {
change.invite(new GradleProjectStructureChangeVisitorAdapter() {
@Override
public void visit(@NotNull GradleMismatchedLibraryPathChange change) {
for (GradleProjectStructureNode<String> holder : myModuleDependencies.values()) {
for (GradleProjectStructureNode<GradleLibraryDependencyId> dependencyNode : holder.getChildren(GradleLibraryDependencyId.class)) {
final GradleLibraryDependencyId id = dependencyNode.getDescriptor().getElement();
if (change.getLibraryName().equals(id.getLibraryName())) {
dependencyNode.addConflictChange(change);
break;
}
}
}
}
@Override
public void visit(@NotNull GradleModulePresenceChange change) {
final GradleModuleId id;
final TextAttributesKey key;
if (change.getGradleEntity() == null) {
id = change.getIntellijEntity();
key = GradleTextAttributes.INTELLIJ_LOCAL_CHANGE;
}
else {
id = change.getGradleEntity();
key = GradleTextAttributes.GRADLE_LOCAL_CHANGE;
}
assert id != null;
final GradleProjectStructureNode<GradleModuleId> moduleNode = getModuleNode(id);
moduleNode.getDescriptor().setAttributes(key);
nodeChanged(moduleNode);
}
@Override
public void visit(@NotNull GradleLibraryDependencyPresenceChange change) {
GradleLibraryDependencyId id = change.getGradleEntity();
TextAttributesKey attributes = GradleTextAttributes.GRADLE_LOCAL_CHANGE;
if (id == null) {
id = change.getIntellijEntity();
attributes = GradleTextAttributes.INTELLIJ_LOCAL_CHANGE;
}
assert id != null;
final GradleProjectStructureNode<String> dependenciesNode = getDependenciesNode(id.getModuleId());
for (GradleProjectStructureNode<GradleLibraryDependencyId> node : dependenciesNode.getChildren(GradleLibraryDependencyId.class)) {
GradleProjectStructureNodeDescriptor<GradleLibraryDependencyId> d = node.getDescriptor();
if (id.equals(d.getElement())) {
d.setAttributes(attributes);
nodeStructureChanged(node);
return;
}
}
GradleProjectStructureNode<GradleLibraryDependencyId> newNode = buildNode(id, id.getLibraryName(), GradleIcons.LIB_ICON);
newNode.getDescriptor().setAttributes(attributes);
dependenciesNode.add(newNode);
nodeStructureChanged(dependenciesNode);
}
});
change.invite(myNewChangesDispatcher);
}
}
@@ -243,84 +190,139 @@ public class GradleProjectStructureTreeModel extends DefaultTreeModel {
*/
public void processObsoleteChanges(Collection<GradleProjectStructureChange> changes) {
for (GradleProjectStructureChange change : changes) {
change.invite(new GradleProjectStructureChangeVisitor() {
@Override
public void visit(@NotNull GradleMismatchedLibraryPathChange change) {
for (GradleProjectStructureNode<String> holder : myModuleDependencies.values()) {
for (GradleProjectStructureNode<GradleLibraryDependencyId> node : holder.getChildren(GradleLibraryDependencyId.class)) {
final GradleLibraryDependencyId id = node.getDescriptor().getElement();
if (id.getLibraryName().equals(change.getLibraryName())) {
node.removeConflictChange(change);
nodeChanged(node);
break;
}
}
}
}
change.invite(myObsoleteChangesDispatcher);
}
}
@Override
public void visit(@NotNull GradleLibraryDependencyPresenceChange change) {
// We need to remove the corresponding node then.
GradleLibraryDependencyId id = change.getGradleEntity();
boolean removeNode;
if (id == null) {
id = change.getIntellijEntity();
assert id != null;
removeNode = !myProjectStructureHelper.isIntellijLibraryDependencyExist(id);
}
else {
removeNode = !myProjectStructureHelper.isGradleLibraryDependencyExist(id);
}
final GradleProjectStructureNode<String> holder = myModuleDependencies.get(id.getModuleName());
if (holder == null) {
return;
}
// There are two possible cases why 'local library dependency' change is obsolete:
// 1. Corresponding dependency has been added at the counterparty;
// 2. The 'local dependency' has been removed;
// We should distinguish between those situations because we need to mark the node as 'synced' at one case and
// completely removed at another one.
for (GradleProjectStructureNode<GradleLibraryDependencyId> node : holder.getChildren(GradleLibraryDependencyId.class)) {
GradleProjectStructureNodeDescriptor<GradleLibraryDependencyId> descriptor = node.getDescriptor();
if (!id.equals(descriptor.getElement())) {
continue;
}
if (removeNode) {
holder.remove(node);
}
else {
descriptor.setAttributes(GradleTextAttributes.GRADLE_NO_CHANGE);
holder.correctChildPositionIfNecessary(node);
}
return;
}
}
private void processNewProjectRenameChange(@NotNull GradleProjectRenameChange change) {
// TODO den implement
}
@Override
public void visit(@NotNull GradleProjectRenameChange change) {
// TODO den implement
}
private void processNewLanguageLevelChange(@NotNull GradleLanguageLevelChange change) {
// TODO den implement
}
@Override
public void visit(@NotNull GradleLanguageLevelChange change) {
// TODO den implement
private void processNewMismatchedLibraryPathChange(@NotNull GradleMismatchedLibraryPathChange change) {
for (GradleProjectStructureNode<String> holder : myModuleDependencies.values()) {
for (GradleProjectStructureNode<GradleLibraryDependencyId> dependencyNode : holder.getChildren(GradleLibraryDependencyId.class)) {
final GradleLibraryDependencyId id = dependencyNode.getDescriptor().getElement();
if (change.getLibraryName().equals(id.getLibraryName())) {
dependencyNode.addConflictChange(change);
break;
}
}
}
}
@Override
public void visit(@NotNull GradleModulePresenceChange change) {
GradleModuleId id = change.getGradleEntity();
if (id == null) {
id = change.getIntellijEntity();
}
assert id != null;
final GradleProjectStructureNode<GradleModuleId> moduleNode = myModules.get(id.getModuleName());
if (moduleNode != null) {
moduleNode.getDescriptor().setAttributes(GradleTextAttributes.GRADLE_NO_CHANGE);
}
private void processNewLibraryDependencyPresenceChange(@NotNull GradleLibraryDependencyPresenceChange change) {
GradleLibraryDependencyId id = change.getGradleEntity();
TextAttributesKey attributes = GradleTextAttributes.GRADLE_LOCAL_CHANGE;
if (id == null) {
id = change.getIntellijEntity();
attributes = GradleTextAttributes.INTELLIJ_LOCAL_CHANGE;
}
assert id != null;
final GradleProjectStructureNode<String> dependenciesNode = getDependenciesNode(id.getModuleId());
for (GradleProjectStructureNode<GradleLibraryDependencyId> node : dependenciesNode.getChildren(GradleLibraryDependencyId.class)) {
GradleProjectStructureNodeDescriptor<GradleLibraryDependencyId> d = node.getDescriptor();
if (id.equals(d.getElement())) {
d.setAttributes(attributes);
nodeStructureChanged(node);
return;
}
}
GradleProjectStructureNode<GradleLibraryDependencyId> newNode = buildNode(id, id.getLibraryName(), GradleIcons.LIB_ICON);
newNode.getDescriptor().setAttributes(attributes);
dependenciesNode.add(newNode);
nodeStructureChanged(dependenciesNode);
}
private void processNewModulePresenceChange(@NotNull GradleModulePresenceChange change) {
final GradleModuleId id;
final TextAttributesKey key;
if (change.getGradleEntity() == null) {
id = change.getIntellijEntity();
key = GradleTextAttributes.INTELLIJ_LOCAL_CHANGE;
}
else {
id = change.getGradleEntity();
key = GradleTextAttributes.GRADLE_LOCAL_CHANGE;
}
assert id != null;
final GradleProjectStructureNode<GradleModuleId> moduleNode = getModuleNode(id);
moduleNode.getDescriptor().setAttributes(key);
nodeChanged(moduleNode);
}
private void processObsoleteProjectRenameChange(@NotNull GradleProjectRenameChange change) {
// TODO den implement
}
private void processObsoleteLanguageLevelChange(@NotNull GradleLanguageLevelChange change) {
// TODO den implement
}
private void processObsoleteMismatchedLibraryPathChange(@NotNull GradleMismatchedLibraryPathChange change) {
for (GradleProjectStructureNode<String> holder : myModuleDependencies.values()) {
for (GradleProjectStructureNode<GradleLibraryDependencyId> node : holder.getChildren(GradleLibraryDependencyId.class)) {
final GradleLibraryDependencyId id = node.getDescriptor().getElement();
if (id.getLibraryName().equals(change.getLibraryName())) {
node.removeConflictChange(change);
nodeChanged(node);
break;
}
});
}
}
}
private void processObsoleteLibraryDependencyPresenceChange(@NotNull GradleLibraryDependencyPresenceChange change) {
// We need to remove the corresponding node then.
GradleLibraryDependencyId id = change.getGradleEntity();
boolean removeNode;
if (id == null) {
id = change.getIntellijEntity();
assert id != null;
removeNode = !myProjectStructureHelper.isIntellijLibraryDependencyExist(id);
}
else {
removeNode = !myProjectStructureHelper.isGradleLibraryDependencyExist(id);
}
final GradleProjectStructureNode<String> holder = myModuleDependencies.get(id.getModuleName());
if (holder == null) {
return;
}
// There are two possible cases why 'local library dependency' change is obsolete:
// 1. Corresponding dependency has been added at the counterparty;
// 2. The 'local dependency' has been removed;
// We should distinguish between those situations because we need to mark the node as 'synced' at one case and
// completely removed at another one.
for (GradleProjectStructureNode<GradleLibraryDependencyId> node : holder.getChildren(GradleLibraryDependencyId.class)) {
GradleProjectStructureNodeDescriptor<GradleLibraryDependencyId> descriptor = node.getDescriptor();
if (!id.equals(descriptor.getElement())) {
continue;
}
if (removeNode) {
holder.remove(node);
}
else {
descriptor.setAttributes(GradleTextAttributes.GRADLE_NO_CHANGE);
holder.correctChildPositionIfNecessary(node);
}
return;
}
}
private void processObsoleteModulePresenceChange(@NotNull GradleModulePresenceChange change) {
GradleModuleId id = change.getGradleEntity();
if (id == null) {
id = change.getIntellijEntity();
}
assert id != null;
final GradleProjectStructureNode<GradleModuleId> moduleNode = myModules.get(id.getModuleName());
if (moduleNode != null) {
moduleNode.getDescriptor().setAttributes(GradleTextAttributes.GRADLE_NO_CHANGE);
}
}
@@ -339,4 +341,22 @@ public class GradleProjectStructureTreeModel extends DefaultTreeModel {
nodesWereRemoved(node.getParent(), myIndexHolder, myNodeHolder);
}
}
private class NewChangesDispatcher implements GradleProjectStructureChangeVisitor {
@Override public void visit(@NotNull GradleProjectRenameChange change) { processNewProjectRenameChange(change); }
@Override public void visit(@NotNull GradleLanguageLevelChange change) { processNewLanguageLevelChange(change); }
@Override public void visit(@NotNull GradleModulePresenceChange change) { processNewModulePresenceChange(change); }
@Override public void visit(@NotNull GradleLibraryDependencyPresenceChange change) { processNewLibraryDependencyPresenceChange(change); }
@Override public void visit(@NotNull GradleMismatchedLibraryPathChange change) { processNewMismatchedLibraryPathChange(change); }
}
private class ObsoleteChangesDispatcher implements GradleProjectStructureChangeVisitor {
@Override public void visit(@NotNull GradleProjectRenameChange change) { processObsoleteProjectRenameChange(change); }
@Override public void visit(@NotNull GradleLanguageLevelChange change) { processObsoleteLanguageLevelChange(change); }
@Override public void visit(@NotNull GradleModulePresenceChange change) { processObsoleteModulePresenceChange(change); }
@Override public void visit(@NotNull GradleLibraryDependencyPresenceChange change) {
processObsoleteLibraryDependencyPresenceChange(change);
}
@Override public void visit(@NotNull GradleMismatchedLibraryPathChange change) { processObsoleteMismatchedLibraryPathChange(change); }
}
}