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

1. Added support for importing module dependencies;
2. Corresponding tests are added;
This commit is contained in:
Denis.Zhdanov
2012-02-21 09:51:09 +04:00
parent e7da7b8ef6
commit 72d28ee49d
33 changed files with 834 additions and 331 deletions
@@ -55,6 +55,7 @@ gradle.sync.change.type.conflict=Settings conflict
gradle.sync.change.type.unchanged=Unchanged setting
gradle.sync.change.type.confirmed=Confirmed settings diff
gradle.sync.change.entity.type.module=module
gradle.sync.change.entity.type.module.dependency=module dependency
gradle.sync.change.entity.type.library.dependency=library dependency
gradle.sync.change.library.path=library ''{0}'' binary path
gradle.sync.change.project.name.text=project name
+1
View File
@@ -38,6 +38,7 @@
<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.GradleModuleDependencyStructureChangesCalculator"/>
<applicationService serviceImplementation="org.jetbrains.plugins.gradle.diff.GradleLibraryStructureChangesCalculator"/>
<applicationService serviceImplementation="org.jetbrains.plugins.gradle.diff.GradleLibraryDependencyStructureChangesCalculator"/>
@@ -0,0 +1,25 @@
package org.jetbrains.plugins.gradle.diff;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.gradle.model.gradle.GradleEntity;
/**
* Manages common dependency properties like 'scope', 'exported'.
* <p/>
* Thread-safe.
*
* @author Denis Zhdanov
* @since 2/20/12 11:17 AM
*/
public abstract class GradleAbstractDependencyStructureChangesCalculator<G extends GradleEntity, I>
implements GradleStructureChangesCalculator<G, I>
{
@Override
public void calculate(@NotNull G gradleEntity, @NotNull I intellijEntity, @NotNull GradleChangesCalculationContext context)
{
// TODO den process common dependency properties here.
doCalculate(gradleEntity, intellijEntity, context);
}
protected abstract void doCalculate(@NotNull G gradleEntity, @NotNull I intellijEntity, @NotNull GradleChangesCalculationContext context);
}
@@ -0,0 +1,53 @@
package org.jetbrains.plugins.gradle.diff;
import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.Set;
/**
* <code>'Parameter object'</code> to use during project structure changes calculations.
* <p/>
* Thread-safe.
*/
public class GradleChangesCalculationContext {
@NotNull private final Set<GradleProjectStructureChange> myKnownChanges = new HashSet<GradleProjectStructureChange>();
@NotNull private final Set<GradleProjectStructureChange> myCurrentChanges = new HashSet<GradleProjectStructureChange>();
@NotNull private final PlatformFacade myPlatformFacade;
/**
* @param knownChanges changes between the gradle and intellij project structure that has been known up until now
* @param platformFacade platform facade to use during the calculations
*/
public GradleChangesCalculationContext(@NotNull Set<GradleProjectStructureChange> knownChanges,
@NotNull PlatformFacade platformFacade)
{
myKnownChanges.addAll(knownChanges);
myPlatformFacade = platformFacade;
}
@NotNull
public Set<GradleProjectStructureChange> getKnownChanges() {
return myKnownChanges;
}
@NotNull
public Set<GradleProjectStructureChange> getCurrentChanges() {
return myCurrentChanges;
}
public void register(@NotNull GradleProjectStructureChange change) {
myCurrentChanges.add(change);
}
public boolean hasNewChanges() {
return !myKnownChanges.equals(myCurrentChanges);
}
@NotNull
public PlatformFacade getPlatformFacade() {
return myPlatformFacade;
}
}
@@ -1,11 +1,17 @@
package org.jetbrains.plugins.gradle.diff;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.LibraryOrderEntry;
import com.intellij.openapi.roots.ModuleOrderEntry;
import com.intellij.openapi.roots.OrderEntry;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.gradle.model.gradle.*;
import org.jetbrains.plugins.gradle.model.intellij.IntellijEntityVisitor;
import org.jetbrains.plugins.gradle.util.GradleUtil;
import java.util.*;
import java.util.HashMap;
import java.util.Map;
/**
* Contains various utility methods for building changes between the gradle and intellij project structures.
@@ -25,10 +31,10 @@ public class GradleDiffUtil {
* Example: particular module has been added at the gradle side. We want to mark that module, its content root(s), dependencies etc
* as gradle-local changes.
*
* @param entity target gradle-local entity
* @param currentChanges holder for the changes built during the current call
* @param entity target gradle-local entity
* @param context changes calculation context to use
*/
public static void buildLocalChanges(@NotNull GradleEntity entity, @NotNull final Set<GradleProjectStructureChange> currentChanges) {
public static void buildLocalChanges(@NotNull GradleEntity entity, @NotNull final GradleChangesCalculationContext context) {
entity.invite(new GradleEntityVisitor() {
@Override
public void visit(@NotNull GradleProject project) {
@@ -37,7 +43,7 @@ public class GradleDiffUtil {
@Override
public void visit(@NotNull GradleModule module) {
currentChanges.add(new GradleModulePresenceChange(module, null));
context.register(new GradleModulePresenceChange(module, null));
for (GradleDependency dependency : module.getDependencies()) {
dependency.invite(this);
}
@@ -55,59 +61,56 @@ public class GradleDiffUtil {
@Override
public void visit(@NotNull GradleModuleDependency dependency) {
// TODO den implement
context.register(new GradleModuleDependencyPresenceChange(dependency, null));
}
@Override
public void visit(@NotNull GradleLibraryDependency dependency) {
currentChanges.add(new GradleLibraryDependencyPresenceChange(dependency, null));
context.register(new GradleLibraryDependencyPresenceChange(dependency, null));
}
});
}
/**
* Analogues to {@link #buildLocalChanges} but targets intellij entity.
*
* @param module target intellij-local module that doesn't present at the gradle side
* @param currentChanges holder for the changes built during the current call
*/
public static void buildLocalChanges(@NotNull Module module,
@NotNull Set<GradleProjectStructureChange> currentChanges)
{
currentChanges.add(new GradleModulePresenceChange(null, module));
// TODO den process module sub-entities here (content roots and dependencies).
}
/**
* Analogues to {@link #buildLocalChanges} but targets intellij entity.
*
* @param libraryDependency target intellij-local library dependency that doesn't present at the gradle side
* @param currentChanges holder for the changes built during the current call
*/
public static void buildLocalChanges(@NotNull LibraryOrderEntry libraryDependency,
@NotNull Set<GradleProjectStructureChange> currentChanges)
{
final String libraryName = libraryDependency.getLibraryName();
if (libraryName != null) {
currentChanges.add(new GradleLibraryDependencyPresenceChange(null, libraryDependency));
}
}
/**
* Performs argument type-based dispatch and delegates to one of strongly typed <code>'buildLocalChanges()'</code> methods.
*
* @param entity target intellij-local entity that doesn't present at the gradle side
* @param currentChanges holder for the changes built during the current call
* @param entity target intellij-local entity that doesn't present at the gradle side
* @param context changes calculation context to use
*/
public static void buildLocalChanges(@NotNull Object entity, @NotNull Set<GradleProjectStructureChange> currentChanges) {
public static void buildLocalChanges(@NotNull Object entity, @NotNull final GradleChangesCalculationContext context) {
if (entity instanceof GradleEntity) {
buildLocalChanges((GradleEntity)entity, currentChanges);
buildLocalChanges((GradleEntity)entity, context);
}
else if (entity instanceof Module) {
buildLocalChanges((Module)entity, currentChanges);
}
else if (entity instanceof LibraryOrderEntry) {
buildLocalChanges((LibraryOrderEntry)entity, currentChanges);
else {
GradleUtil.dispatch(entity, new IntellijEntityVisitor() {
@Override
public void visit(@NotNull Project project) {
}
@Override
public void visit(@NotNull Module module) {
context.register(new GradleModulePresenceChange(null, module));
for (OrderEntry entry : context.getPlatformFacade().getOrderEntries(module)) {
GradleUtil.dispatch(entry, this);
}
}
@Override
public void visit(@NotNull LibraryOrderEntry libraryDependency) {
final String libraryName = libraryDependency.getLibraryName();
if (libraryName != null) {
context.register(new GradleLibraryDependencyPresenceChange(null, libraryDependency));
}
}
@Override
public void visit(@NotNull ModuleOrderEntry moduleDependency) {
final Module module = moduleDependency.getModule();
if (module != null) {
context.register(new GradleModuleDependencyPresenceChange(null, moduleDependency));
}
}
});
}
}
@@ -120,8 +123,7 @@ public class GradleDiffUtil {
* @param calculator comparison strategy that works with the single entities (not collection of entities)
* @param gradleEntities entities available at the gradle side
* @param intellijEntities entities available at the intellij side
* @param knownChanges collection that contains known changes about the entities
* @param currentChanges holder for the changes discovered during the current call
* @param context changes calculation context
* @param <I> target intellij entity type
* @param <G> target gradle entity type
*/
@@ -129,8 +131,7 @@ public class GradleDiffUtil {
@NotNull GradleStructureChangesCalculator<G, I> calculator,
@NotNull Iterable<? extends G> gradleEntities,
@NotNull Iterable<? extends I> intellijEntities,
@NotNull Set<GradleProjectStructureChange> knownChanges,
@NotNull Set<GradleProjectStructureChange> currentChanges)
@NotNull GradleChangesCalculationContext context)
{
Map<Object, I> intellijEntitiesByKeys = new HashMap<Object, I>();
for (I entity : intellijEntities) {
@@ -138,17 +139,17 @@ public class GradleDiffUtil {
assert previous == null;
}
for (G gradleEntity: gradleEntities) {
I intellijEntity = intellijEntitiesByKeys.remove(calculator.getGradleKey(gradleEntity, knownChanges));
I intellijEntity = intellijEntitiesByKeys.remove(calculator.getGradleKey(gradleEntity, context));
if (intellijEntity == null) {
buildLocalChanges(gradleEntity, currentChanges);
buildLocalChanges(gradleEntity, context);
}
else {
calculator.calculate(gradleEntity, intellijEntity, knownChanges, currentChanges);
calculator.calculate(gradleEntity, intellijEntity, context);
}
}
for (I entity : intellijEntitiesByKeys.values()) {
buildLocalChanges(entity, currentChanges);
buildLocalChanges(entity, context);
}
}
}
@@ -5,14 +5,12 @@ import com.intellij.openapi.roots.libraries.Library;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.gradle.model.gradle.GradleLibraryDependency;
import java.util.Set;
/**
* @author Denis Zhdanov
* @since 1/24/12 1:37 PM
*/
public class GradleLibraryDependencyStructureChangesCalculator
implements GradleStructureChangesCalculator<GradleLibraryDependency, LibraryOrderEntry>
extends GradleAbstractDependencyStructureChangesCalculator<GradleLibraryDependency, LibraryOrderEntry>
{
private final GradleLibraryStructureChangesCalculator myLibraryCalculator;
@@ -22,28 +20,30 @@ public class GradleLibraryDependencyStructureChangesCalculator
}
@Override
public void calculate(@NotNull GradleLibraryDependency gradleEntity,
@NotNull LibraryOrderEntry intellijEntity,
@NotNull Set<GradleProjectStructureChange> knownChanges,
@NotNull Set<GradleProjectStructureChange> currentChanges)
public void doCalculate(@NotNull GradleLibraryDependency gradleEntity,
@NotNull LibraryOrderEntry intellijEntity,
@NotNull GradleChangesCalculationContext context)
{
final Library library = intellijEntity.getLibrary();
if (library == null) {
return;
}
myLibraryCalculator.calculate(gradleEntity.getTarget(), library, knownChanges, currentChanges);
myLibraryCalculator.calculate(gradleEntity.getTarget(), library, context);
}
@NotNull
@Override
public Object getIntellijKey(@NotNull LibraryOrderEntry entity) {
final String result = entity.getLibraryName();
return result == null ? "" : result;
final Library library = entity.getLibrary();
if (library == null) {
return "";
}
return myLibraryCalculator.getIntellijKey(library);
}
@NotNull
@Override
public Object getGradleKey(@NotNull GradleLibraryDependency entity, @NotNull Set<GradleProjectStructureChange> knownChanges) {
return myLibraryCalculator.getGradleKey(entity.getTarget(), knownChanges);
public Object getGradleKey(@NotNull GradleLibraryDependency entity, @NotNull GradleChangesCalculationContext context) {
return myLibraryCalculator.getGradleKey(entity.getTarget(), context);
}
}
@@ -25,8 +25,7 @@ public class GradleLibraryStructureChangesCalculator implements GradleStructureC
@Override
public void calculate(@NotNull GradleLibrary gradleEntity,
@NotNull Library intellijEntity,
@NotNull Set<GradleProjectStructureChange> knownChanges,
@NotNull Set<GradleProjectStructureChange> currentChanges)
@NotNull GradleChangesCalculationContext context)
{
final Set<String> gradleBinaryPaths = new HashSet<String>(gradleEntity.getPaths(LibraryPathType.BINARY));
final Set<String> intellijBinaryPaths = new HashSet<String>();
@@ -38,7 +37,7 @@ public class GradleLibraryStructureChangesCalculator implements GradleStructureC
}
if (!gradleBinaryPaths.equals(intellijBinaryPaths)) {
currentChanges.add(new GradleMismatchedLibraryPathChange(intellijEntity, gradleBinaryPaths, intellijBinaryPaths));
context.register(new GradleMismatchedLibraryPathChange(intellijEntity, gradleBinaryPaths, intellijBinaryPaths));
}
}
@@ -50,7 +49,7 @@ public class GradleLibraryStructureChangesCalculator implements GradleStructureC
@NotNull
@Override
public Object getGradleKey(@NotNull GradleLibrary entity, @NotNull Set<GradleProjectStructureChange> knownChanges) {
public Object getGradleKey(@NotNull GradleLibrary entity, @NotNull GradleChangesCalculationContext context) {
// TODO den consider the known changes
return entity.getName();
}
@@ -0,0 +1,43 @@
package org.jetbrains.plugins.gradle.diff;
import com.intellij.openapi.roots.ModuleOrderEntry;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.gradle.model.gradle.GradleModuleDependency;
import org.jetbrains.plugins.gradle.model.id.GradleEntityIdMapper;
import org.jetbrains.plugins.gradle.model.id.GradleModuleDependencyId;
import org.jetbrains.plugins.gradle.util.GradleBundle;
/**
* @author Denis Zhdanov
* @since 2/17/12 3:14 PM
*/
public class GradleModuleDependencyPresenceChange extends GradleEntityPresenceChange<GradleModuleDependencyId> {
public GradleModuleDependencyPresenceChange(@Nullable GradleModuleDependency gradle,
@Nullable ModuleOrderEntry intellij)
{
super(GradleBundle.message("gradle.sync.change.entity.type.module.dependency"), of(gradle), of(intellij));
}
@Override
public void invite(@NotNull GradleProjectStructureChangeVisitor visitor) {
visitor.visit(this);
}
@Nullable
private static GradleModuleDependencyId of(@Nullable GradleModuleDependency dependency) {
if (dependency == null) {
return null;
}
return GradleEntityIdMapper.mapEntityToId(dependency);
}
@Nullable
private static GradleModuleDependencyId of(@Nullable ModuleOrderEntry dependency) {
if (dependency == null) {
return null;
}
return GradleEntityIdMapper.mapEntityToId(dependency);
}
}
@@ -0,0 +1,39 @@
package org.jetbrains.plugins.gradle.diff;
import com.intellij.openapi.roots.ModuleOrderEntry;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.gradle.model.gradle.GradleModuleDependency;
/**
* @author Denis Zhdanov
* @since 2/20/12 11:10 AM
*/
public class GradleModuleDependencyStructureChangesCalculator
extends GradleAbstractDependencyStructureChangesCalculator<GradleModuleDependency, ModuleOrderEntry>
{
@Override
public void doCalculate(@NotNull GradleModuleDependency gradleEntity,
@NotNull ModuleOrderEntry intellijEntity,
@NotNull GradleChangesCalculationContext context)
{
// Assuming that the modules referenced by the given dependencies are compared independently.
}
@NotNull
@Override
public Object getIntellijKey(@NotNull ModuleOrderEntry entity) {
final String intellijModuleName = entity.getModuleName();
if (intellijModuleName == null) {
return "";
}
return intellijModuleName;
}
@NotNull
@Override
public Object getGradleKey(@NotNull GradleModuleDependency entity, @NotNull GradleChangesCalculationContext context) {
// TODO den consider known changes here.
return entity.getTarget().getName();
}
}
@@ -16,13 +16,15 @@
package org.jetbrains.plugins.gradle.diff;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.roots.*;
import com.intellij.openapi.roots.LibraryOrderEntry;
import com.intellij.openapi.roots.ModuleOrderEntry;
import com.intellij.openapi.roots.OrderEntry;
import com.intellij.openapi.roots.RootPolicy;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.gradle.model.gradle.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* Encapsulates functionality of calculating changes between Gradle and IntelliJ IDEA module hierarchies.
@@ -34,25 +36,24 @@ import java.util.Set;
*/
public class GradleModuleStructureChangesCalculator implements GradleStructureChangesCalculator<GradleModule, Module> {
private final GradleLibraryDependencyStructureChangesCalculator myLibraryDependencyCalculator;
private final PlatformFacade myStructureHelper;
@NotNull private final GradleLibraryDependencyStructureChangesCalculator myLibraryDependencyCalculator;
@NotNull private final GradleModuleDependencyStructureChangesCalculator myModuleDependencyCalculator;
public GradleModuleStructureChangesCalculator(@NotNull GradleLibraryDependencyStructureChangesCalculator libraryDependencyCalculator,
@NotNull PlatformFacade structureHelper)
@NotNull GradleModuleDependencyStructureChangesCalculator moduleDependencyCalculator)
{
myLibraryDependencyCalculator = libraryDependencyCalculator;
myStructureHelper = structureHelper;
myModuleDependencyCalculator = moduleDependencyCalculator;
}
@Override
public void calculate(@NotNull GradleModule gradleEntity,
@NotNull Module intellijEntity,
@NotNull Set<GradleProjectStructureChange> knownChanges,
@NotNull Set<GradleProjectStructureChange> currentChanges)
@NotNull GradleChangesCalculationContext context)
{
//TODO den process module-local settings
//TODO den process content roots
checkDependencies(gradleEntity, intellijEntity, knownChanges, currentChanges);
checkDependencies(gradleEntity, intellijEntity, context);
}
@NotNull
@@ -63,15 +64,14 @@ public class GradleModuleStructureChangesCalculator implements GradleStructureCh
@NotNull
@Override
public Object getGradleKey(@NotNull GradleModule entity, @NotNull Set<GradleProjectStructureChange> knownChanges) {
public Object getGradleKey(@NotNull GradleModule entity, @NotNull GradleChangesCalculationContext context) {
// TODO den consider the known changes
return entity.getName();
}
private void checkDependencies(@NotNull GradleModule gradleModule,
@NotNull Module intellijModule,
@NotNull Set<GradleProjectStructureChange> knownChanges,
@NotNull Set<GradleProjectStructureChange> currentChanges)
@NotNull GradleChangesCalculationContext context)
{
// Prepare intellij part.
final List<ModuleOrderEntry> intellijModuleDependencies = new ArrayList<ModuleOrderEntry>();
@@ -89,7 +89,7 @@ public class GradleModuleStructureChangesCalculator implements GradleStructureCh
return libraryOrderEntry;
}
};
for (OrderEntry orderEntry : myStructureHelper.getOrderEntries(intellijModule)) {
for (OrderEntry orderEntry : context.getPlatformFacade().getOrderEntries(intellijModule)) {
orderEntry.accept(policy, null);
}
@@ -112,8 +112,7 @@ public class GradleModuleStructureChangesCalculator implements GradleStructureCh
}
// Calculate changes.
// TODO den process module dependencies here as well.
GradleDiffUtil.calculate(myLibraryDependencyCalculator, gradleLibraryDependencies, intellijLibraryDependencies,
knownChanges, currentChanges);
GradleDiffUtil.calculate(myLibraryDependencyCalculator, gradleLibraryDependencies, intellijLibraryDependencies, context);
GradleDiffUtil.calculate(myModuleDependencyCalculator, gradleModuleDependencies, intellijModuleDependencies, context);
}
}
@@ -28,5 +28,6 @@ public interface GradleProjectStructureChangeVisitor {
void visit(@NotNull GradleLanguageLevelChange change);
void visit(@NotNull GradleModulePresenceChange change);
void visit(@NotNull GradleLibraryDependencyPresenceChange change);
void visit(@NotNull GradleModuleDependencyPresenceChange change);
void visit(@NotNull GradleMismatchedLibraryPathChange change);
}
@@ -1,45 +0,0 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.gradle.diff;
import org.jetbrains.annotations.NotNull;
/**
* @author Denis Zhdanov
* @since 11/16/11 8:49 PM
*/
public class GradleProjectStructureChangeVisitorAdapter implements GradleProjectStructureChangeVisitor {
@Override
public void visit(@NotNull GradleProjectRenameChange change) {
}
@Override
public void visit(@NotNull GradleLanguageLevelChange change) {
}
@Override
public void visit(@NotNull GradleModulePresenceChange change) {
}
@Override
public void visit(@NotNull GradleLibraryDependencyPresenceChange change) {
}
@Override
public void visit(@NotNull GradleMismatchedLibraryPathChange change) {
}
}
@@ -21,25 +21,24 @@ import java.util.Set;
public class GradleProjectStructureChangesCalculator implements GradleStructureChangesCalculator<GradleProject, Project> {
private final GradleModuleStructureChangesCalculator myModuleChangesCalculator;
private final PlatformFacade myStructureHelper;
private final PlatformFacade myPlatformFacade;
public GradleProjectStructureChangesCalculator(@NotNull GradleModuleStructureChangesCalculator moduleCalculator,
@NotNull PlatformFacade structureHelper) {
@NotNull PlatformFacade platformFacade) {
myModuleChangesCalculator = moduleCalculator;
myStructureHelper = structureHelper;
myPlatformFacade = platformFacade;
}
@Override
public void calculate(@NotNull GradleProject gradleEntity,
@NotNull Project intellijEntity,
@NotNull Set<GradleProjectStructureChange> knownChanges,
@NotNull Set<GradleProjectStructureChange> currentChanges)
@NotNull GradleChangesCalculationContext context)
{
calculateProjectChanges(gradleEntity, intellijEntity, currentChanges);
calculateProjectChanges(gradleEntity, intellijEntity, context.getCurrentChanges());
final Set<? extends GradleModule> gradleSubEntities = gradleEntity.getModules();
final Collection<Module> intellijSubEntities = myStructureHelper.getModules(intellijEntity);
GradleDiffUtil.calculate(myModuleChangesCalculator, gradleSubEntities, intellijSubEntities, knownChanges, currentChanges);
final Collection<Module> intellijSubEntities = myPlatformFacade.getModules(intellijEntity);
GradleDiffUtil.calculate(myModuleChangesCalculator, gradleSubEntities, intellijSubEntities, context);
}
@NotNull
@@ -50,7 +49,7 @@ public class GradleProjectStructureChangesCalculator implements GradleStructureC
@NotNull
@Override
public Object getGradleKey(@NotNull GradleProject entity, @NotNull Set<GradleProjectStructureChange> knownChanges) {
public Object getGradleKey(@NotNull GradleProject entity, @NotNull GradleChangesCalculationContext context) {
// TODO den consider the known changes
return entity.getName();
}
@@ -79,7 +78,7 @@ public class GradleProjectStructureChangesCalculator implements GradleStructureC
@NotNull Set<GradleProjectStructureChange> currentChanges)
{
LanguageLevel gradleLevel = gradleProject.getLanguageLevel();
LanguageLevel intellijLevel = myStructureHelper.getLanguageLevel(intellijProject);
LanguageLevel intellijLevel = myPlatformFacade.getLanguageLevel(intellijProject);
if (gradleLevel != intellijLevel) {
currentChanges.add(new GradleLanguageLevelChange(gradleLevel, intellijLevel));
}
@@ -3,8 +3,6 @@ package org.jetbrains.plugins.gradle.diff;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.gradle.model.gradle.GradleEntity;
import java.util.Set;
/**
* Defines common interface to the strategy that calculates difference between the corresponding gradle and intellij entities
* (e.g. between the gradle and intellij module).
@@ -20,18 +18,12 @@ public interface GradleStructureChangesCalculator<G extends GradleEntity, I> {
/**
* Calculates changes between the given entities.
*
*
* @param gradleEntity target gradle entity
* @param intellijEntity target intellij entity
* @param knownChanges changes between the gradle and intellij project structure that has been known up until now
* @param currentChanges holder for the changes between the given entities discovered by the current call. Note that
* it must contain the change objects that have been known (contained at the <code>'knownChanges'</code>)
* but are still in place
* @param context target diff calculation context
*/
void calculate(@NotNull G gradleEntity,
@NotNull I intellijEntity,
@NotNull Set<GradleProjectStructureChange> knownChanges,
@NotNull Set<GradleProjectStructureChange> currentChanges);
void calculate(@NotNull G gradleEntity, @NotNull I intellijEntity, @NotNull GradleChangesCalculationContext context);
/**
* There are three possible situations when we compare a set of gradle entities with a set of intellij entities:
@@ -43,16 +35,16 @@ public interface GradleStructureChangesCalculator<G extends GradleEntity, I> {
* </ul>
* </pre>
* <p/>
* The general idea is to map evey item at the given sets of gradle and intellij entities to particular key (both gradle and
* The general idea is to map every item at the given sets of gradle and intellij entities to particular key (both gradle and
* intellij keys are expected to belong to the same class) and then compare them. Matched keys shows that corresponding
* entities should be {@link #calculate(GradleEntity, Object, Set, Set)} compared to each other}; non-matched indicate that corresponding
* entities are gradle- or intellij-local.
* entities should be {@link #calculate(GradleEntity, Object, GradleChangesCalculationContext) compared to each other};
* non-matched indicate that corresponding entities are gradle- or intellij-local.
* <p/>
* This method allows to match intellij entity to the target key.
*
* @param entity intellij entity to match
* @return key for the given entity
* @see #getGradleKey(GradleEntity, Set)
* @see #getGradleKey(GradleEntity, GradleChangesCalculationContext)
*/
@NotNull
Object getIntellijKey(@NotNull I entity);
@@ -68,10 +60,10 @@ public interface GradleStructureChangesCalculator<G extends GradleEntity, I> {
* So, we take into consideration the known changes only during {@code gradle-entity -> key} processing.
*
* @param entity target gradle entity that should be mapped to a key
* @param knownChanges known changes between the gradle and intellij structures
* @param context changes calculation context
* @return key for the given entity
* @see #getIntellijKey(Object)
*/
@NotNull
Object getGradleKey(@NotNull G entity, @NotNull Set<GradleProjectStructureChange> knownChanges);
Object getGradleKey(@NotNull G entity, @NotNull GradleChangesCalculationContext context);
}
@@ -11,7 +11,7 @@ import javax.swing.*;
*/
public enum GradleEntityType {
PROJECT(GradleIcons.PROJECT_ICON), MODULE(GradleIcons.MODULE_ICON), MODULE_DEPENDENCY(GradleIcons.MODULE_ICON),
LIBRARY_DEPENDENCY(GradleIcons.LIB_ICON), CONTENT_ROOT(GradleIcons.CONTENT_ROOT_ICON), SYNTHETIC(null);
LIBRARY(GradleIcons.LIB_ICON), LIBRARY_DEPENDENCY(GradleIcons.LIB_ICON), CONTENT_ROOT(GradleIcons.CONTENT_ROOT_ICON), SYNTHETIC(null);
@Nullable private final Icon myIcon;
@@ -89,6 +89,10 @@ public class GradleModule extends AbstractNamedGradleEntity implements Named {
myDependencies.add(dependency);
}
public void clearDependencies() {
myDependencies.clear();
}
@Override
public void invite(@NotNull GradleEntityVisitor visitor) {
visitor.visit(this);
@@ -0,0 +1,55 @@
package org.jetbrains.plugins.gradle.model.id;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.gradle.model.GradleEntityOwner;
import org.jetbrains.plugins.gradle.model.GradleEntityType;
/**
* @author Denis Zhdanov
* @since 2/20/12 12:01 PM
*/
public abstract class GradleAbstractDependencyId extends GradleAbstractEntityId {
@NotNull private final String myOwnerModuleName;
@NotNull private final String myDependencyName;
public GradleAbstractDependencyId(@NotNull GradleEntityType type,
@NotNull GradleEntityOwner owner,
@NotNull String ownerModuleName,
@NotNull String dependencyName)
{
super(type, owner);
myOwnerModuleName = ownerModuleName;
myDependencyName = dependencyName;
}
@NotNull
public String getOwnerModuleName() {
return myOwnerModuleName;
}
@NotNull
public String getDependencyName() {
return myDependencyName;
}
@NotNull
public GradleModuleId getOwnerModuleId() {
return new GradleModuleId(getOwner(), myOwnerModuleName);
}
@Override
public int hashCode() {
int result = 31 * super.hashCode() + myOwnerModuleName.hashCode();
return 31 * result + myDependencyName.hashCode();
}
@Override
public boolean equals(Object o) {
if (!super.equals(o)) {
return false;
}
GradleAbstractDependencyId that = (GradleAbstractDependencyId)o;
return myOwnerModuleName.equals(that.myOwnerModuleName) && myDependencyName.equals(that.myDependencyName);
}
}
@@ -59,7 +59,7 @@ public class GradleEntityIdMapper {
@Override
public void visit(@NotNull GradleModuleDependency dependency) {
result.set(new GradleLibraryDependencyId(GradleEntityOwner.GRADLE, dependency.getOwnerModule().getName(), dependency.getName()));
result.set(new GradleModuleDependencyId(GradleEntityOwner.GRADLE, dependency.getOwnerModule().getName(), dependency.getName()));
}
@Override
@@ -74,7 +74,7 @@ public class GradleEntityIdMapper {
@Override
public void visit(@NotNull GradleLibrary library) {
// TODO den implement
result.set(new GradleLibraryId(GradleEntityOwner.GRADLE, library.getName()));
}
});
}
@@ -8,60 +8,26 @@ import org.jetbrains.plugins.gradle.model.GradleEntityType;
* @author Denis Zhdanov
* @since 2/14/12 1:59 PM
*/
public class GradleLibraryDependencyId extends GradleAbstractEntityId {
public class GradleLibraryDependencyId extends GradleAbstractDependencyId {
@NotNull private final String myModuleName;
@NotNull private final String myLibraryName;
public GradleLibraryDependencyId(@NotNull GradleEntityOwner owner, @NotNull String moduleName, @NotNull String libraryName) {
super(GradleEntityType.LIBRARY_DEPENDENCY, owner);
myModuleName = moduleName;
myLibraryName = libraryName;
super(GradleEntityType.LIBRARY_DEPENDENCY, owner, moduleName, libraryName);
}
@NotNull
public String getModuleName() {
return myModuleName;
}
@NotNull
public String getLibraryName() {
return myLibraryName;
}
@NotNull
public GradleModuleId getModuleId() {
return new GradleModuleId(getOwner(), myModuleName);
}
@Override
public Object mapToEntity(@NotNull GradleEntityMappingContext context) {
switch (getOwner()) {
case GRADLE: return context.getProjectStructureHelper().findGradleLibraryDependency(myModuleName, myLibraryName);
case INTELLIJ: return context.getProjectStructureHelper().findIntellijLibraryDependency(myModuleName, myLibraryName);
case GRADLE: return context.getProjectStructureHelper().findGradleLibraryDependency(getOwnerModuleName(), getDependencyName());
case INTELLIJ: return context.getProjectStructureHelper().findIntellijLibraryDependency(getOwnerModuleName(), getDependencyName());
}
throw new IllegalStateException(String.format(
"Can't map id to the target library dependency. Owner: %s, module: '%s', library: '%s'", getOwner(), myModuleName, myLibraryName
"Can't map id to the target library dependency. Owner: %s, module: '%s', library: '%s'",
getOwner(), getOwnerModuleName(), getDependencyName()
));
}
@Override
public int hashCode() {
int result = 31 * super.hashCode() + myModuleName.hashCode();
return 31 * result + myLibraryName.hashCode();
}
@Override
public boolean equals(Object o) {
if (!super.equals(o)) {
return false;
}
GradleLibraryDependencyId that = (GradleLibraryDependencyId)o;
return myModuleName.equals(that.myModuleName) && myLibraryName.equals(that.myLibraryName);
}
@Override
public String toString() {
return String.format("library dependency:owner module='%s'|library='%s'", myModuleName, myLibraryName);
return String.format("library dependency:owner module='%s'|library='%s'", getOwnerModuleName(), getDependencyName());
}
}
@@ -0,0 +1,49 @@
package org.jetbrains.plugins.gradle.model.id;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.gradle.model.GradleEntityOwner;
import org.jetbrains.plugins.gradle.model.GradleEntityType;
/**
* @author Denis Zhdanov
* @since 2/20/12 7:12 PM
*/
public class GradleLibraryId extends GradleAbstractEntityId {
@NotNull private final String myLibraryName;
public GradleLibraryId(@NotNull GradleEntityOwner owner, @NotNull String libraryName) {
super(GradleEntityType.LIBRARY, owner);
myLibraryName = libraryName;
}
@Override
public Object mapToEntity(@NotNull GradleEntityMappingContext context) {
switch (getOwner()) {
case GRADLE: return context.getProjectStructureHelper().findGradleLibrary(myLibraryName);
case INTELLIJ: return context.getProjectStructureHelper().findIntellijLibrary(myLibraryName);
}
throw new IllegalStateException(String.format(
"Can't map library id to the target library. Id owner: %s, name: '%s'", getOwner(), myLibraryName
));
}
@Override
public int hashCode() {
return 31 * super.hashCode() + myLibraryName.hashCode();
}
@Override
public boolean equals(Object o) {
if (!super.equals(o)) {
return false;
}
GradleLibraryId that = (GradleLibraryId)o;
return myLibraryName.equals(that.myLibraryName);
}
@Override
public String toString() {
return "library '" + myLibraryName + "'";
}
}
@@ -8,46 +8,26 @@ import org.jetbrains.plugins.gradle.model.GradleEntityType;
* @author Denis Zhdanov
* @since 2/14/12 2:06 PM
*/
public class GradleModuleDependencyId extends GradleAbstractEntityId {
public class GradleModuleDependencyId extends GradleAbstractDependencyId {
@NotNull private final String myOwnerModuleName;
@NotNull private final String myDependencyModuleName;
public GradleModuleDependencyId(@NotNull GradleEntityOwner owner, @NotNull String ownerModuleName, @NotNull String dependencyModuleName) {
super(GradleEntityType.MODULE, owner);
myOwnerModuleName = ownerModuleName;
myDependencyModuleName = dependencyModuleName;
super(GradleEntityType.MODULE_DEPENDENCY, owner, ownerModuleName, dependencyModuleName);
}
@Override
public Object mapToEntity(@NotNull GradleEntityMappingContext context) {
switch (getOwner()) {
case GRADLE: return context.getProjectStructureHelper().findGradleModuleDependency(myOwnerModuleName, myDependencyModuleName);
case INTELLIJ: return context.getProjectStructureHelper().findIntellijModuleDependency(myOwnerModuleName, myDependencyModuleName);
case GRADLE: return context.getProjectStructureHelper().findGradleModuleDependency(getOwnerModuleName(), getDependencyName());
case INTELLIJ: return context.getProjectStructureHelper().findIntellijModuleDependency(getOwnerModuleName(), getDependencyName());
}
throw new IllegalStateException(String.format(
"Can't map id to the target module dependency. Owner: %s, owner module: '%s', dependency module: '%s'",
getOwner(), myOwnerModuleName, myDependencyModuleName
getOwner(), getOwnerModuleName(), getDependencyName()
));
}
@Override
public int hashCode() {
int result = 31 * super.hashCode() + myOwnerModuleName.hashCode();
return 31 * result + myDependencyModuleName.hashCode();
}
@Override
public boolean equals(Object o) {
if (!super.equals(o)) {
return false;
}
GradleModuleDependencyId that = (GradleModuleDependencyId)o;
return myOwnerModuleName.equals(that.myOwnerModuleName) && myDependencyModuleName.equals(that.myDependencyModuleName);
}
@Override
public String toString() {
return String.format("module dependency:owner module='%s'|dependency module='%s'", myOwnerModuleName, myDependencyModuleName);
return String.format("module dependency:owner module='%s'|dependency module='%s'", getOwnerModuleName(), getDependencyName());
}
}
@@ -46,4 +46,9 @@ public class GradleModuleId extends GradleAbstractEntityId {
GradleModuleId that = (GradleModuleId)o;
return myModuleName.equals(that.myModuleName);
}
@Override
public String toString() {
return "module '" + myModuleName + "'";
}
}
@@ -4,8 +4,10 @@ import com.intellij.openapi.components.AbstractProjectComponent;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.gradle.diff.GradleChangesCalculationContext;
import org.jetbrains.plugins.gradle.diff.GradleProjectStructureChange;
import org.jetbrains.plugins.gradle.diff.GradleStructureChangesCalculator;
import org.jetbrains.plugins.gradle.diff.PlatformFacade;
import org.jetbrains.plugins.gradle.model.gradle.GradleProject;
import java.util.HashSet;
@@ -28,14 +30,17 @@ public class GradleProjectStructureChangesModel extends AbstractProjectComponent
= new AtomicReference<Set<GradleProjectStructureChange>>(new HashSet<GradleProjectStructureChange>());
private final AtomicReference<GradleProject> myGradleProject = new AtomicReference<GradleProject>();
private final GradleStructureChangesCalculator<GradleProject, Project> myChangesCalculator;
@NotNull private final GradleStructureChangesCalculator<GradleProject, Project> myChangesCalculator;
@NotNull private final PlatformFacade myPlatformFacade;
public GradleProjectStructureChangesModel(@NotNull Project project,
@NotNull GradleStructureChangesCalculator<GradleProject, Project> changesCalculator)
@NotNull GradleStructureChangesCalculator<GradleProject, Project> changesCalculator,
@NotNull PlatformFacade platformFacade)
{
super(project);
myChangesCalculator = changesCalculator;
myPlatformFacade = platformFacade;
}
/**
@@ -58,15 +63,14 @@ public class GradleProjectStructureChangesModel extends AbstractProjectComponent
*/
public void update(@NotNull GradleProject gradleProject) {
myGradleProject.set(gradleProject);
Set<GradleProjectStructureChange> knownChanges = new HashSet<GradleProjectStructureChange>(myChanges.get());
Set<GradleProjectStructureChange> currentChanges = new HashSet<GradleProjectStructureChange>();
myChangesCalculator.calculate(gradleProject, myProject, knownChanges, currentChanges);
if (currentChanges.equals(knownChanges)) {
GradleChangesCalculationContext context = new GradleChangesCalculationContext(myChanges.get(), myPlatformFacade);
myChangesCalculator.calculate(gradleProject, myProject, context);
if (!context.hasNewChanges()) {
return;
}
myChanges.set(currentChanges);
myChanges.set(context.getCurrentChanges());
for (GradleProjectStructureChangeListener listener : myListeners) {
listener.onChanges(knownChanges, currentChanges);
listener.onChanges(context.getKnownChanges(), context.getCurrentChanges());
}
}
@@ -15,6 +15,7 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.gradle.diff.PlatformFacade;
import org.jetbrains.plugins.gradle.model.gradle.*;
import org.jetbrains.plugins.gradle.model.id.GradleLibraryDependencyId;
import org.jetbrains.plugins.gradle.model.id.GradleModuleDependencyId;
/**
* Thread-safe.
@@ -49,23 +50,7 @@ public class GradleProjectStructureHelper extends AbstractProjectComponent {
* <code>false</code> otherwise
*/
public boolean isIntellijLibraryDependencyExist(@NotNull final GradleLibraryDependencyId id) {
final Module module = findIntellijModule(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());
}
};
for (OrderEntry entry : myFacade.getOrderEntries(module)) {
if (entry.accept(visitor, false)) {
return true;
}
}
return false;
return findIntellijLibraryDependency(id.getOwnerModuleName(), id.getDependencyName()) != null;
}
/**
@@ -76,7 +61,15 @@ public class GradleProjectStructureHelper extends AbstractProjectComponent {
* <code>false</code> otherwise
*/
public boolean isGradleLibraryDependencyExist(@NotNull final GradleLibraryDependencyId id) {
return findGradleLibraryDependency(id.getModuleName(), id.getLibraryName()) != null;
return findGradleLibraryDependency(id.getOwnerModuleName(), id.getDependencyName()) != null;
}
public boolean isIntellijModuleDependencyExist(@NotNull final GradleModuleDependencyId id) {
return findIntellijModuleDependency(id.getOwnerModuleName(), id.getDependencyName()) != null;
}
public boolean isGradleModuleDependencyExist(@NotNull final GradleModuleDependencyId id) {
return findIntellijModuleDependency(id.getOwnerModuleName(), id.getDependencyName()) != null;
}
@Nullable
@@ -111,10 +104,15 @@ public class GradleProjectStructureHelper extends AbstractProjectComponent {
@Nullable
public Library findIntellijLibrary(@NotNull final GradleLibrary library) {
return findIntellijLibrary(library.getName());
}
@Nullable
public Library findIntellijLibrary(@NotNull String libraryName) {
final LibraryTable libraryTable = myFacade.getProjectLibraryTable(myProject);
for (Library intellijLibrary : libraryTable.getLibraries()) {
// TODO den consider 'merged libraries' at the registered project structure changes here.
if (library.getName().equals(intellijLibrary.getName())) {
if (libraryName.equals(intellijLibrary.getName())) {
return intellijLibrary;
}
}
@@ -145,6 +143,20 @@ public class GradleProjectStructureHelper extends AbstractProjectComponent {
}
return null;
}
@Nullable
public GradleLibrary findGradleLibrary(@NotNull final String libraryName) {
final GradleProject project = myModel.getGradleProject();
if (project == null) {
return null;
}
for (GradleLibrary library : project.getLibraries()) {
if (libraryName.equals(library.getName())) {
return library;
}
}
return null;
}
@Nullable
public GradleLibraryDependency findGradleLibraryDependency(@NotNull final String moduleName, @NotNull final String libraryName) {
@@ -92,7 +92,7 @@ public class GradleProjectStructureTreeModel extends DefaultTreeModel {
return value;
}
GradleLibraryDependencyId id = GradleEntityIdMapper.mapEntityToId(libraryOrderEntry);
dependencies.add(buildNode(id, id.getLibraryName()));
dependencies.add(buildNode(id, id.getDependencyName()));
return value;
}
};
@@ -137,6 +137,7 @@ public class GradleProjectStructureTreeModel extends DefaultTreeModel {
GradleProjectStructureNode<GradleModuleId> moduleNode = getModuleNode(id);
GradleProjectStructureNode<GradleSyntheticId> result
= new GradleProjectStructureNode<GradleSyntheticId>(GradleConstants.DEPENDENCIES_NODE_DESCRIPTOR);
result.addListener(myNodeListener);
moduleNode.add(result);
myModuleDependencies.put(id.getModuleName(), result);
@@ -196,7 +197,7 @@ public class GradleProjectStructureTreeModel extends DefaultTreeModel {
for (GradleProjectStructureNode<GradleSyntheticId> holder : myModuleDependencies.values()) {
for (GradleProjectStructureNode<GradleLibraryDependencyId> dependencyNode : holder.getChildren(GradleLibraryDependencyId.class)) {
final GradleLibraryDependencyId id = dependencyNode.getDescriptor().getElement();
if (change.getLibraryName().equals(id.getLibraryName())) {
if (change.getLibraryName().equals(id.getDependencyName())) {
dependencyNode.addConflictChange(change);
break;
}
@@ -205,25 +206,31 @@ public class GradleProjectStructureTreeModel extends DefaultTreeModel {
}
private void processNewLibraryDependencyPresenceChange(@NotNull GradleLibraryDependencyPresenceChange change) {
GradleLibraryDependencyId id = change.getGradleEntity();
processNewDependencyPresenceChange(change);
}
private void processNewModuleDependencyPresenceChange(@NotNull GradleModuleDependencyPresenceChange change) {
processNewDependencyPresenceChange(change);
}
private <I extends GradleAbstractDependencyId> void processNewDependencyPresenceChange(@NotNull GradleEntityPresenceChange<I> change) {
I 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<GradleSyntheticId> 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);
final GradleProjectStructureNode<GradleSyntheticId> dependenciesNode = getDependenciesNode(id.getOwnerModuleId());
for (GradleProjectStructureNode<? extends GradleAbstractDependencyId> node : dependenciesNode.getChildren(id.getClass())) {
if (id.equals(node.getDescriptor().getElement())) {
node.setAttributes(attributes);
return;
}
}
GradleProjectStructureNode<GradleLibraryDependencyId> newNode = buildNode(id, id.getLibraryName());
newNode.setAttributes(attributes);
GradleProjectStructureNode<I> newNode = buildNode(id, id.getDependencyName());
dependenciesNode.add(newNode);
newNode.setAttributes(attributes);
}
private void processNewModulePresenceChange(@NotNull GradleModulePresenceChange change) {
@@ -254,7 +261,7 @@ public class GradleProjectStructureTreeModel extends DefaultTreeModel {
for (GradleProjectStructureNode<GradleSyntheticId> holder : myModuleDependencies.values()) {
for (GradleProjectStructureNode<GradleLibraryDependencyId> node : holder.getChildren(GradleLibraryDependencyId.class)) {
final GradleLibraryDependencyId id = node.getDescriptor().getElement();
if (id.getLibraryName().equals(change.getLibraryName())) {
if (id.getDependencyName().equals(change.getLibraryName())) {
node.removeConflictChange(change);
break;
}
@@ -274,7 +281,25 @@ public class GradleProjectStructureTreeModel extends DefaultTreeModel {
else {
removeNode = !myProjectStructureHelper.isGradleLibraryDependencyExist(id);
}
final GradleProjectStructureNode<GradleSyntheticId> holder = myModuleDependencies.get(id.getModuleName());
processObsoleteDependencyPresenceChange(id, removeNode);
}
private void processObsoleteModuleDependencyPresenceChange(@NotNull GradleModuleDependencyPresenceChange change) {
GradleModuleDependencyId id = change.getGradleEntity();
boolean removeNode;
if (id == null) {
id = change.getIntellijEntity();
assert id != null;
removeNode = !myProjectStructureHelper.isIntellijModuleDependencyExist(id);
}
else {
removeNode = !myProjectStructureHelper.isGradleModuleDependencyExist(id);
}
processObsoleteDependencyPresenceChange(id, removeNode);
}
private void processObsoleteDependencyPresenceChange(@NotNull GradleAbstractDependencyId id, boolean removeNode) {
final GradleProjectStructureNode<GradleSyntheticId> holder = myModuleDependencies.get(id.getOwnerModuleName());
if (holder == null) {
return;
}
@@ -285,8 +310,8 @@ public class GradleProjectStructureTreeModel extends DefaultTreeModel {
// 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();
for (GradleProjectStructureNode<? extends GradleAbstractDependencyId> node : holder.getChildren(id.getClass())) {
GradleProjectStructureNodeDescriptor<? extends GradleAbstractDependencyId> descriptor = node.getDescriptor();
if (!id.equals(descriptor.getElement())) {
continue;
}
@@ -303,12 +328,31 @@ public class GradleProjectStructureTreeModel extends DefaultTreeModel {
private void processObsoleteModulePresenceChange(@NotNull GradleModulePresenceChange change) {
GradleModuleId id = change.getGradleEntity();
boolean removeNode;
if (id == null) {
id = change.getIntellijEntity();
assert id != null;
removeNode = myProjectStructureHelper.findIntellijModule(id.getModuleName()) == null;
}
assert id != null;
else {
removeNode = myProjectStructureHelper.findGradleModule(id.getModuleName()) == null;
}
// There are two possible cases why 'module presence' change is obsolete:
// 1. Corresponding module has been added at the counterparty;
// 2. The 'local module' 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.
final GradleProjectStructureNode<GradleModuleId> moduleNode = myModules.get(id.getModuleName());
if (moduleNode != null) {
if (moduleNode == null) {
return;
}
if (removeNode) {
moduleNode.removeFromParent();
}
else {
moduleNode.getDescriptor().setAttributes(GradleTextAttributes.GRADLE_NO_CHANGE);
}
}
@@ -322,10 +366,13 @@ public class GradleProjectStructureTreeModel extends DefaultTreeModel {
}
@Override
public void onNodeRemoved(@NotNull GradleProjectStructureNode<?> node, int index) {
myIndexHolder[0] = index;
myNodeHolder[0] = node;
nodesWereRemoved(node.getParent(), myIndexHolder, myNodeHolder);
public void onNodeRemoved(@NotNull GradleProjectStructureNode<?> parent,
@NotNull GradleProjectStructureNode<?> removedChild,
int removedChildIndex)
{
myIndexHolder[0] = removedChildIndex;
myNodeHolder[0] = removedChild;
nodesWereRemoved(parent, myIndexHolder, myNodeHolder);
}
@Override
@@ -339,6 +386,8 @@ public class GradleProjectStructureTreeModel extends DefaultTreeModel {
@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 GradleModuleDependencyPresenceChange change) { processNewModuleDependencyPresenceChange(change); }
@Override public void visit(@NotNull GradleMismatchedLibraryPathChange change) { processNewMismatchedLibraryPathChange(change); }
}
@@ -349,6 +398,9 @@ public class GradleProjectStructureTreeModel extends DefaultTreeModel {
@Override public void visit(@NotNull GradleLibraryDependencyPresenceChange change) {
processObsoleteLibraryDependencyPresenceChange(change);
}
@Override public void visit(@NotNull GradleModuleDependencyPresenceChange change) {
processObsoleteModuleDependencyPresenceChange(change);
}
@Override public void visit(@NotNull GradleMismatchedLibraryPathChange change) { processObsoleteMismatchedLibraryPathChange(change); }
}
}
@@ -39,7 +39,7 @@ public class GradleResolveProjectTask extends AbstractGradleTask {
final GradleProject project = resolver.resolveProjectInfo(getId(), myProjectPath, myResolveLibraries);
myGradleProject.set(project);
setState(GradleTaskState.FINISHED);
if (myIntellijProject == null) {
if (myIntellijProject == null || myIntellijProject.isDisposed()) {
return;
}
final GradleProjectStructureChangesModel model = myIntellijProject.getComponent(GradleProjectStructureChangesModel.class);
@@ -70,13 +70,11 @@ public class GradleProjectStructureNode<T extends GradleEntityId> extends Defaul
for (int i = 0; i < getChildCount(); i++) {
GradleProjectStructureNode<?> node = getChildAt(i);
if (NODE_COMPARATOR.compare((GradleProjectStructureNode<?>)newChild, node) <= 0) {
insert(newChild, i);
onNodeAdded((GradleProjectStructureNode<?>)newChild, i);
insert(newChild, i); // Assuming that the node listeners are notified during the nested call.
return;
}
}
super.add(newChild);
onNodeAdded((GradleProjectStructureNode<?>)newChild, getChildCount() - 1);
super.add(newChild); // Assuming that the node listeners are notified during the nested call to 'insert()'.
}
@Override
@@ -108,7 +106,7 @@ public class GradleProjectStructureNode<T extends GradleEntityId> extends Defaul
*/
public void correctChildPositionIfNecessary(@NotNull GradleProjectStructureNode<?> child) {
int currentPosition = -1;
int desiredPosition = getChildCount() - 1;
int desiredPosition = getChildCount();
for (int i = 0; i < getChildCount(); i++) {
GradleProjectStructureNode<?> node = getChildAt(i);
if (node == child) {
@@ -117,12 +115,21 @@ public class GradleProjectStructureNode<T extends GradleEntityId> extends Defaul
}
if (NODE_COMPARATOR.compare(child, node) <= 0) {
desiredPosition = i;
if (currentPosition >= 0) {
break;
}
}
}
if (currentPosition < 0) {
// Given node is not a child of the current node.
return;
}
if (currentPosition < desiredPosition) {
desiredPosition--;
}
if (currentPosition == desiredPosition) {
return;
}
remove(currentPosition);
insert(child, desiredPosition);
}
@@ -237,9 +244,9 @@ public class GradleProjectStructureNode<T extends GradleEntityId> extends Defaul
}
}
private void onNodeRemoved(@NotNull GradleProjectStructureNode<?> node, int index) {
private void onNodeRemoved(@NotNull GradleProjectStructureNode<?> node, int removedChildIndex) {
for (Listener listener : myListeners) {
listener.onNodeRemoved(node, index);
listener.onNodeRemoved(this, node, removedChildIndex);
}
}
@@ -251,7 +258,9 @@ public class GradleProjectStructureNode<T extends GradleEntityId> extends Defaul
public interface Listener {
void onNodeAdded(@NotNull GradleProjectStructureNode<?> node, int index);
void onNodeRemoved(@NotNull GradleProjectStructureNode<?> node, int index);
void onNodeRemoved(@NotNull GradleProjectStructureNode<?> parent,
@NotNull GradleProjectStructureNode<?> removedChild,
int removedChildIndex);
void onNodeChanged(@NotNull GradleProjectStructureNode<?> node);
}
}
@@ -40,8 +40,8 @@ public class GradleProjectStructureChangesModelTest extends AbstractGradleTest {
// Check that the initial projects state is correctly parsed.
checkChanges {
presence {
library(gradle: gradle.modules.dependencies.flatten().findAll { it.name == "lib2" })
library(gradle: gradle.modules.dependencies.flatten().findAll { it.name == "lib3" })
library(gradle: gradle.libraryDependencies.values().flatten().findAll { it.name == "lib2" })
library(gradle: gradle.libraryDependencies.values().flatten().findAll { it.name == "lib3" })
} }
checkTree {
project {
@@ -63,7 +63,7 @@ public class GradleProjectStructureChangesModelTest extends AbstractGradleTest {
checkChanges {
presence {
library(gradle: gradle.modules.dependencies.flatten().findAll { it.name == "lib3" })
library(gradle: gradle.libraryDependencies.values().flatten().findAll { it.name == "lib3" })
} }
checkTree {
project {
@@ -144,7 +144,7 @@ public class GradleProjectStructureChangesModelTest extends AbstractGradleTest {
} } } }
)
checkChanges {
libraryConflict(entity: intellij.dependencies.values().flatten().find {it.library.name == 'lib2' }.library) {
libraryConflict(entity: intellij.libraryDependencies.values().flatten().find {it.library.name == 'lib2' }.library) {
binaryPath(gradle: null, intellij: '3')
} }
checkTree {
@@ -209,8 +209,8 @@ public class GradleProjectStructureChangesModelTest extends AbstractGradleTest {
} } )
checkChanges {
presence {
module(gradle: gradle.modules.find { it.name == 'module2'})
libraryDependency(gradle: gradle.modules.dependencies.flatten())
module(gradle: gradle.modules['module2'])
libraryDependency(gradle: gradle.modules['module2'].dependencies)
} }
checkTree {
project {
@@ -237,7 +237,7 @@ public class GradleProjectStructureChangesModelTest extends AbstractGradleTest {
)
checkChanges {
presence {
module(gradle: gradle.modules.find { it.name == 'module2' })
module(gradle: gradle.modules['module2'])
} }
checkTree {
project {
@@ -286,8 +286,8 @@ public class GradleProjectStructureChangesModelTest extends AbstractGradleTest {
checkChanges {
presence {
module(gradle: gradle.modules.find { it.name == 'module2' })
libraryDependency(gradle: gradle.dependencies[gradle.modules.find { it.name == 'module2' }].first())
module(gradle: gradle.modules['module2'])
libraryDependency(gradle: gradle.libraryDependencies[gradle.modules['module2']].first())
}
libraryConflict(entity: intellij.libraries['lib1']) {
binaryPath(gradle: ['1'], intellij: ['2'])
@@ -303,4 +303,181 @@ public class GradleProjectStructureChangesModelTest extends AbstractGradleTest {
lib1('gradle') // This is the point of the test. We don't expect to see 'conflict' here.
} } } }
}
@Test
public void "mismatched module dependency import"() {
init(
gradle: {
project {
module('module1')
module('module2')
module('module3') {
dependencies {
module('module1')
module('module2')
} } } },
intellij: {
project {
module('module2')
module('module4')
module('module3') {
dependencies {
module('module2')
module('module4')
} } } }
)
checkChanges {
presence {
module(gradle: gradle.modules['module1'])
module(intellij: intellij.modules['module4'])
moduleDependency(gradle: gradle.moduleDependencies[gradle.modules['module3']].find { it.target == gradle.modules['module1']})
moduleDependency(intellij: intellij.moduleDependencies[intellij.modules['module3']].find { it.moduleName == 'module4'})
} }
checkTree {
project {
module1('gradle')
module2()
module3() {
dependencies {
module1('gradle')
module2()
module4('intellij')
} }
module4('intellij')
} }
Closure newProjectState = {
project {
module('module1')
module('module2')
module('module4')
module('module3') {
dependencies {
module('module1')
module('module2')
module('module4')
} } } }
setState(gradle: newProjectState, intellij: newProjectState)
checkChanges { } // No changes
checkTree {
project {
module1()
module2()
module3 {
dependencies {
module1()
module2()
module4()
} }
module4()
} }
}
@Test
public void "mismatched module dependency removal"() {
init(
gradle: {
project {
module('module1')
module('module2')
module('module3') {
dependencies {
module('module1')
module('module2')
} } } },
intellij: {
project {
module('module2')
module('module4')
module('module3') {
dependencies {
module('module2')
module('module4')
} } } }
)
checkChanges {
presence {
module(gradle: gradle.modules['module1'])
module(intellij: intellij.modules['module4'])
moduleDependency(gradle: gradle.moduleDependencies[gradle.modules['module3']].find { it.target == gradle.modules['module1']})
moduleDependency(intellij: intellij.moduleDependencies[intellij.modules['module3']].find { it.moduleName == 'module4'})
} }
checkTree {
project {
module1('gradle')
module2()
module3() {
dependencies {
module1('gradle')
module2()
module4('intellij')
} }
module4('intellij')
} }
Closure newProjectState = {
project {
module('module2')
module('module3') {
dependencies {
module('module2')
} } } }
setState(gradle: newProjectState, intellij: newProjectState)
checkChanges { } // No changes
checkTree {
project {
module2()
module3 {
dependencies {
module2()
} } } }
}
@Test
public void "cycled module dependencies"() {
init(
gradle: {
project {
module('module1') {
dependencies {
module('module2')
} }
module('module2') {
dependencies {
module('module1')
} } } },
intellij: {
project {
module('module2') {
dependencies {
module('module3')
} }
module('module3') {
dependencies {
module('module2')
} } } }
)
checkChanges {
presence {
module(gradle: gradle.modules['module1'])
module(intellij: intellij.modules['module3'])
moduleDependency(gradle: gradle.moduleDependencies.values().flatten())
moduleDependency(intellij: intellij.moduleDependencies.values().flatten())
} }
checkTree {
project {
module1('gradle') {
dependencies {
module2('gradle')
} }
module2() {
dependencies {
module1('gradle')
module3('intellij')
} }
module3('intellij') {
dependencies {
module2('intellij')
} } } }
}
}
@@ -1,23 +1,18 @@
package org.jetbrains.plugins.gradle.testutil
import com.intellij.openapi.project.Project
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.plugins.gradle.model.id.GradleEntityIdMapper
import org.jetbrains.plugins.gradle.sync.GradleProjectStructureChangeListener
import org.jetbrains.plugins.gradle.sync.GradleProjectStructureChangesModel
import org.jetbrains.plugins.gradle.sync.GradleProjectStructureHelper
import org.jetbrains.plugins.gradle.sync.GradleProjectStructureTreeModel
import org.junit.Before
import org.picocontainer.defaults.DefaultPicoContainer
import com.intellij.openapi.project.Project
import org.jetbrains.plugins.gradle.diff.PlatformFacade
import org.jetbrains.plugins.gradle.diff.GradleStructureChangesCalculator
import org.jetbrains.plugins.gradle.diff.GradleProjectStructureChangesCalculator
import org.jetbrains.plugins.gradle.diff.GradleModuleStructureChangesCalculator
import org.jetbrains.plugins.gradle.diff.GradleLibraryDependencyStructureChangesCalculator
import org.jetbrains.plugins.gradle.diff.GradleLibraryStructureChangesCalculator
import org.jetbrains.plugins.gradle.sync.GradleProjectStructureHelper
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.plugins.gradle.sync.GradleProjectStructureChangeListener
import static org.junit.Assert.assertEquals
import org.picocontainer.MutablePicoContainer
import org.jetbrains.plugins.gradle.model.id.GradleEntityIdMapper;
import org.picocontainer.defaults.DefaultPicoContainer
import org.jetbrains.plugins.gradle.diff.*
import static org.junit.Assert.fail
/**
* @author Denis Zhdanov
@@ -48,6 +43,7 @@ public abstract class AbstractGradleTest {
container.registerComponentImplementation(GradleProjectStructureHelper)
container.registerComponentImplementation(GradleStructureChangesCalculator, GradleProjectStructureChangesCalculator)
container.registerComponentImplementation(GradleModuleStructureChangesCalculator)
container.registerComponentImplementation(GradleModuleDependencyStructureChangesCalculator)
container.registerComponentImplementation(GradleLibraryDependencyStructureChangesCalculator)
container.registerComponentImplementation(GradleLibraryStructureChangesCalculator)
container.registerComponentImplementation(GradleEntityIdMapper)
@@ -96,7 +92,22 @@ public abstract class AbstractGradleTest {
if (!expected) {
expected = [].toSet()
}
assertEquals(expected, changesModel.changes)
def actual = new HashSet(changesModel.changes)
if (expected == actual) {
return
}
actual.removeAll(expected)
expected.removeAll(changesModel.changes)
def message = "Project structure changes are mismatched."
if (expected) {
message += "\n Expected but not matched:"
expected.each { message += "\n * $it"}
}
if (actual) {
message += "\n Unexpected:"
actual.each { message += "\n * $it"}
}
fail(message)
}
protected def checkTree(c) {
@@ -1,6 +1,7 @@
package org.jetbrains.plugins.gradle.testutil
import com.intellij.pom.java.LanguageLevel
import com.intellij.openapi.util.text.StringUtil
/**
* @author Denis Zhdanov
@@ -12,16 +13,28 @@ public abstract class AbstractProjectBuilder extends BuilderSupport {
private static int COUNTER
def project
def modules = []
/** [module name; module] */
def modules = [:]
/** Holds (library name; library) pairs for the active configuration. */
def libraries = [:]
/** [module; dependency list] */
def libraryDependencies = [:].withDefault {[]}
/** [module; dependency list] */
def moduleDependencies = [:].withDefault {[]}
/**
* Holds (library name; library) pairs for the whole test. I.e. there is a possible case that we define particular configuration
* initially and the adjust it. We need to use the same library instance then in order to pass hashCode()/equals() checks then.
* This map works as a test-wide storage.
*/
def librariesCache = [:]
def dependencies = [:].withDefault {[]}
/** [module name; module] */
def modulesCache = [:]
@Override
protected void setParent(Object parent, Object child) {
@@ -43,37 +56,54 @@ public abstract class AbstractProjectBuilder extends BuilderSupport {
}
@Override
protected Object createNode(Object name, Map attributes) {
protected Object createNode(name, Map attributes) {
switch (name) {
case "dependencies": return current // Assuming that 'current' is a module object
case "project":
reset()
clear()
return project = createProject(attributes.name?: same, attributes.langLevel?: LanguageLevel.JDK_1_6)
case "module": def module = createModule(attributes.name?: same); modules << module; return module
case "module":
case "library":
def n = StringUtil.capitalize(name)
if (current == project) {
// Library.
return getLibrary(attributes)
}
else {
// Library dependency.
def module = current
def dep = createLibraryDependency(module, getLibrary(attributes))
dependencies[module] << dep
return dep
// Not a dependency.
return "get$n"(attributes)
}
def ownerModule = current
def dep = "create${n}Dependency"(ownerModule, "get$n"(attributes))
"get${n}Dependencies"()[ownerModule] << dep
return dep
}
}
protected abstract def createProject(String name, LanguageLevel languageLevel)
protected abstract def createModule(String name)
protected abstract def registerModule(module)
protected abstract def createLibrary(String name, Map paths)
protected abstract def applyLibraryPaths(library, Map paths)
protected abstract def createLibraryDependency(module, library)
protected abstract def createModuleDependency(ownerModule, targetModule)
protected abstract def reset();
protected String getUnique() { "./${COUNTER++}" }
protected String getSame() { SAME_TOKEN }
private def getModule(Map attributes) {
def name = attributes.name?: same
def result = modules[name]
if (result) return result
result = modulesCache[name]
if (result) {
registerModule(result)
}
else {
result = createModule(name)
modulesCache[name] = result
}
modules[name] = result
result
}
private def getLibrary(Map attributes) {
def name = attributes.name?: same
def result = libraries[name]
@@ -89,8 +119,9 @@ public abstract class AbstractProjectBuilder extends BuilderSupport {
libraries[name] = result
result
}
private def reset() {
[modules, dependencies, libraries]*.clear()
private def clear() {
reset()
[modules, libraryDependencies, libraries, moduleDependencies]*.clear()
}
}
@@ -6,6 +6,7 @@ import org.jetbrains.plugins.gradle.diff.GradleMismatchedLibraryPathChange
import com.intellij.openapi.roots.libraries.Library
import org.jetbrains.plugins.gradle.util.GradleUtil
import org.jetbrains.plugins.gradle.diff.GradleModulePresenceChange
import org.jetbrains.plugins.gradle.diff.GradleModuleDependencyPresenceChange
/**
* @author Denis Zhdanov
@@ -34,6 +35,10 @@ public class ChangeBuilder extends BuilderSupport {
changes.addAll attributes.gradle.collect { new GradleModulePresenceChange(it, null)}
changes.addAll attributes.intellij.collect { new GradleModulePresenceChange(null, it)}
return changes
case "moduleDependency":
changes.addAll attributes.gradle.collect { new GradleModuleDependencyPresenceChange(it, null) }
changes.addAll attributes.intellij.collect { new GradleModuleDependencyPresenceChange(null, it) }
return changes
case "library":
changes.addAll attributes.gradle.collect { new GradleLibraryDependencyPresenceChange(it, null)}
changes.addAll attributes.intellij.collect { new GradleLibraryDependencyPresenceChange(null, it)}
@@ -52,7 +57,7 @@ public class ChangeBuilder extends BuilderSupport {
// Assuming that we're processing library binary path conflict here
register(new GradleMismatchedLibraryPathChange(
current as Library, collectPaths(attributes.gradle), collectPaths(attributes.intellij)
))
))
}
changes
}
@@ -6,6 +6,7 @@ import org.jetbrains.plugins.gradle.model.gradle.GradleLibraryDependency
import org.jetbrains.plugins.gradle.model.gradle.GradleLibrary
import org.jetbrains.plugins.gradle.model.gradle.LibraryPathType
import com.intellij.pom.java.LanguageLevel
import org.jetbrains.plugins.gradle.model.gradle.GradleModuleDependency
/**
* @author Denis Zhdanov
@@ -23,8 +24,19 @@ class GradleProjectBuilder extends AbstractProjectBuilder {
@Override
protected createModule(String name) {
def result = new GradleModule(name, unique)
project.addModule(result)
registerModule(new GradleModule(name, unique))
}
@Override
protected registerModule(module) {
project.addModule(module)
module
}
@Override
protected createModuleDependency(ownerModule, targetModule) {
def result = new GradleModuleDependency(ownerModule, targetModule)
ownerModule.addDependency(result)
result
}
@@ -49,4 +61,9 @@ class GradleProjectBuilder extends AbstractProjectBuilder {
key, type -> paths[key]?.each { library.addPath(type, it) }
}
}
@Override
protected reset() {
modulesCache.values().each { it.clearDependencies() }
}
}
@@ -13,6 +13,7 @@ import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.roots.OrderRootType
import org.jetbrains.plugins.gradle.util.GradleUtil
import com.intellij.openapi.roots.libraries.LibraryTable
import com.intellij.openapi.roots.ModuleOrderEntry
/**
* @author Denis Zhdanov
@@ -30,8 +31,8 @@ class IntellijProjectBuilder extends AbstractProjectBuilder {
def projectLibraryTable = projectLibraryTableStub as LibraryTable
def platformFacade = [
getModules: { modules },
getOrderEntries: { dependencies[it] },
getModules: { modules.values() },
getOrderEntries: { libraryDependencies[it] + moduleDependencies[it] },
getProjectIcon: { IconLoader.getIcon("/nodes/ideaProject.png") },
getLocalFileSystemPath: { it.path },
getProjectLibraryTable: { projectLibraryTable }
@@ -42,7 +43,7 @@ class IntellijProjectBuilder extends AbstractProjectBuilder {
@Override
protected createProject(String name, LanguageLevel languageLevel) {
projectStub.getName = { name }
platformFacade.getLanguageLevel = { languageLevel }
platformFacade.getLanguageLevel = { languageLevel } as Closure
project
}
@@ -51,6 +52,20 @@ class IntellijProjectBuilder extends AbstractProjectBuilder {
[ getName: { name } ] as Module
}
@Override
protected registerModule(Object module) { }
@Override
protected createModuleDependency(ownerModule, targetModule) {
def stub = [:]
def result = stub as ModuleOrderEntry
stub.accept = { policy, defaultValue -> policy.visitModuleOrderEntry(result, defaultValue) }
stub.getModule = { targetModule }
stub.getOwnerModule = { ownerModule }
stub.getModuleName = { targetModule.name }
result
}
@Override
protected createLibrary(String name, Map paths) {
libraryPaths[name] = paths
@@ -81,4 +96,7 @@ class IntellijProjectBuilder extends AbstractProjectBuilder {
protected applyLibraryPaths(library, Map paths) {
libraryPaths[library.name] = paths
}
@Override
protected reset() { }
}