org.gradle.plugins.ide.idea.model.internal.IdeaDependenciesProvider.java Source code

Java tutorial

Introduction

Here is the source code for org.gradle.plugins.ide.idea.model.internal.IdeaDependenciesProvider.java

Source

/*
 * Copyright 2011 the original author or authors.
 *
 * 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.gradle.plugins.ide.idea.model.internal;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.*;
import org.gradle.api.Nullable;
import org.gradle.api.Transformer;
import org.gradle.api.artifacts.Configuration;
import org.gradle.plugins.ide.idea.model.Dependency;
import org.gradle.plugins.ide.idea.model.FilePath;
import org.gradle.plugins.ide.idea.model.IdeaModule;
import org.gradle.plugins.ide.idea.model.SingleEntryModuleLibrary;
import org.gradle.plugins.ide.internal.IdeDependenciesExtractor;
import org.gradle.plugins.ide.internal.resolver.model.IdeDependencyKey;
import org.gradle.plugins.ide.internal.resolver.model.IdeExtendedRepoFileDependency;
import org.gradle.plugins.ide.internal.resolver.model.IdeLocalFileDependency;
import org.gradle.plugins.ide.internal.resolver.model.IdeProjectDependency;
import org.gradle.util.CollectionUtils;

import java.io.File;
import java.util.*;

public class IdeaDependenciesProvider {

    private final IdeDependenciesExtractor dependenciesExtractor;
    private Transformer<FilePath, File> getPath;

    /**
     * List of mappings used to assign IDEA classpath scope to project dependencies.
     *
     * Applied in order: if a dependency is found in all listed configurations it is provided as
     * a dependency in given scope(s).
     */
    Map<GeneratedIdeaScope, List<IdeaScopeMappingRule>> scopeMappings = new EnumMap<GeneratedIdeaScope, List<IdeaScopeMappingRule>>(
            GeneratedIdeaScope.class);

    public IdeaDependenciesProvider() {
        this(new IdeDependenciesExtractor());
    }

    IdeaDependenciesProvider(IdeDependenciesExtractor dependenciesExtractor) {
        this.dependenciesExtractor = dependenciesExtractor;
        scopeMappings.put(GeneratedIdeaScope.PROVIDED_TEST,
                Collections.singletonList(new IdeaScopeMappingRule("providedRuntime", "test")));
        scopeMappings.put(GeneratedIdeaScope.PROVIDED, Lists.newArrayList(
                new IdeaScopeMappingRule("providedCompile"), new IdeaScopeMappingRule("providedRuntime")));
        scopeMappings.put(GeneratedIdeaScope.COMPILE,
                Collections.singletonList(new IdeaScopeMappingRule("compile")));
        scopeMappings.put(GeneratedIdeaScope.RUNTIME_TEST,
                Collections.singletonList(new IdeaScopeMappingRule("testCompile", "runtime")));
        scopeMappings.put(GeneratedIdeaScope.RUNTIME,
                Collections.singletonList(new IdeaScopeMappingRule("runtime")));
        scopeMappings.put(GeneratedIdeaScope.TEST, Lists.newArrayList(new IdeaScopeMappingRule("testCompile"),
                new IdeaScopeMappingRule("testRuntime")));
    }

    Set<Dependency> provide(final IdeaModule ideaModule) {
        getPath = new Transformer<FilePath, File>() {
            @Nullable
            public FilePath transform(File file) {
                return file != null ? ideaModule.getPathFactory().path(file) : null;
            }
        };

        Set<Dependency> result = new LinkedHashSet<Dependency>();
        if (ideaModule.getSingleEntryLibraries() != null) {
            for (Map.Entry<String, Iterable<File>> singleEntryLibrary : ideaModule.getSingleEntryLibraries()
                    .entrySet()) {
                String scope = singleEntryLibrary.getKey();
                for (File file : singleEntryLibrary.getValue()) {
                    if (file != null && file.isDirectory()) {
                        result.add(new SingleEntryModuleLibrary(getPath.transform(file), scope));
                    }
                }
            }
        }
        result.addAll(provideFromScopeRuleMappings(ideaModule));
        return result;
    }

    private Set<Dependency> provideFromScopeRuleMappings(IdeaModule ideaModule) {
        Multimap<IdeDependencyKey<?, Dependency>, String> dependencyToConfigurations = LinkedHashMultimap.create();
        for (Configuration configuration : ideaConfigurations(ideaModule)) {
            // project dependencies
            Collection<IdeProjectDependency> ideProjectDependencies = dependenciesExtractor
                    .extractProjectDependencies(ideaModule.getProject(), Collections.singletonList(configuration),
                            Collections.<Configuration>emptyList());
            for (IdeProjectDependency ideProjectDependency : ideProjectDependencies) {
                IdeDependencyKey<?, Dependency> key = IdeDependencyKey.forProjectDependency(ideProjectDependency,
                        new IdeDependencyKey.DependencyBuilder<IdeProjectDependency, Dependency>() {
                            public Dependency buildDependency(IdeProjectDependency dependency, String scope) {
                                return new ModuleDependencyBuilder().create(dependency.getProject(), scope);
                            }
                        });
                dependencyToConfigurations.put(key, configuration.getName());
            }
            // repository dependencies
            if (!ideaModule.isOffline()) {
                Collection<IdeExtendedRepoFileDependency> ideRepoFileDependencies = dependenciesExtractor
                        .extractRepoFileDependencies(ideaModule.getProject().getDependencies(),
                                Collections.singletonList(configuration), Collections.<Configuration>emptyList(),
                                ideaModule.isDownloadSources(), ideaModule.isDownloadJavadoc());
                for (IdeExtendedRepoFileDependency ideRepoFileDependency : ideRepoFileDependencies) {
                    IdeDependencyKey<?, Dependency> key = IdeDependencyKey.forRepoFileDependency(
                            ideRepoFileDependency,
                            new IdeDependencyKey.DependencyBuilder<IdeExtendedRepoFileDependency, Dependency>() {
                                public Dependency buildDependency(IdeExtendedRepoFileDependency dependency,
                                        String scope) {
                                    Set<FilePath> javadoc = CollectionUtils.collect(dependency.getJavadocFiles(),
                                            new LinkedHashSet<FilePath>(), getPath);
                                    Set<FilePath> source = CollectionUtils.collect(dependency.getSourceFiles(),
                                            new LinkedHashSet<FilePath>(), getPath);
                                    SingleEntryModuleLibrary library = new SingleEntryModuleLibrary(
                                            getPath.transform(dependency.getFile()), javadoc, source, scope);
                                    library.setModuleVersion(dependency.getId());
                                    return library;
                                }
                            });
                    dependencyToConfigurations.put(key, configuration.getName());
                }
            }
            // file dependencies
            Collection<IdeLocalFileDependency> ideLocalFileDependencies = dependenciesExtractor
                    .extractLocalFileDependencies(Collections.singletonList(configuration),
                            Collections.<Configuration>emptyList());
            for (IdeLocalFileDependency fileDependency : ideLocalFileDependencies) {
                IdeDependencyKey<?, Dependency> key = IdeDependencyKey.forLocalFileDependency(fileDependency,
                        new IdeDependencyKey.DependencyBuilder<IdeLocalFileDependency, Dependency>() {
                            public Dependency buildDependency(IdeLocalFileDependency dependency, String scope) {
                                return new SingleEntryModuleLibrary(getPath.transform(dependency.getFile()), scope);
                            }
                        });
                dependencyToConfigurations.put(key, configuration.getName());
            }
        }

        Set<Dependency> dependencies = new LinkedHashSet<Dependency>();
        for (GeneratedIdeaScope scope : GeneratedIdeaScope.values()) {
            Map<String, Collection<Configuration>> plusMinusConfigurations = ideaModule.getScopes()
                    .get(scope.name());
            if (plusMinusConfigurations == null) {
                if (shouldProcessScope(scope, ideaModule.getScopes())) {
                    plusMinusConfigurations = Collections.emptyMap();
                } else {
                    continue;
                }
            }
            Collection<Configuration> minusConfigurations = plusMinusConfigurations.get("minus");
            Collection<String> minusConfigurationNames = minusConfigurations != null ? Lists
                    .newArrayList(Iterables.transform(minusConfigurations, new Function<Configuration, String>() {
                        public String apply(Configuration configuration) {
                            return configuration.getName();
                        }
                    })) : Collections.<String>emptyList();

            for (IdeaScopeMappingRule scopeMappingRule : scopeMappings.get(scope)) {
                Collection<IdeDependencyKey<?, Dependency>> matchingDependencies = extractDependencies(
                        dependencyToConfigurations, scopeMappingRule.configurationNames, minusConfigurationNames);
                for (final IdeDependencyKey<?, Dependency> dependencyKey : matchingDependencies) {
                    dependencies.addAll(Lists
                            .newArrayList(Iterables.transform(scope.scopes, scopeToDependency(dependencyKey))));
                }
            }
            if (plusMinusConfigurations.containsKey("plus")) {
                for (Configuration plusConfiguration : plusMinusConfigurations.get("plus")) {
                    Collection<IdeDependencyKey<?, Dependency>> matchingDependencies = extractDependencies(
                            dependencyToConfigurations, Collections.singletonList(plusConfiguration.getName()),
                            minusConfigurationNames);
                    for (IdeDependencyKey<?, Dependency> dependencyKey : matchingDependencies) {
                        dependencies.addAll(Lists
                                .newArrayList(Iterables.transform(scope.scopes, scopeToDependency(dependencyKey))));
                    }
                }
            }
        }
        return dependencies;
    }

    private boolean shouldProcessScope(GeneratedIdeaScope scope,
            Map<String, Map<String, Collection<Configuration>>> scopes) {
        // composite scopes are not present in IdeaModule.scopes - check their mapped scope names
        for (String scopeName : scope.scopes) {
            if (!scopes.containsKey(scopeName)) {
                return false;
            }
        }
        return true;
    }

    private static Function<String, Dependency> scopeToDependency(
            final IdeDependencyKey<?, Dependency> dependencyKey) {
        return new Function<String, Dependency>() {
            @Nullable
            public Dependency apply(String s) {
                return dependencyKey.buildDependency(s);
            }
        };
    }

    private Iterable<Configuration> ideaConfigurations(final IdeaModule ideaModule) {
        Set<Configuration> configurations = Sets.newHashSet(ideaModule.getProject().getConfigurations());
        for (Map<String, Collection<Configuration>> scopeMap : ideaModule.getScopes().values()) {
            for (Configuration cfg : Iterables.concat(scopeMap.values())) {
                configurations.add(cfg);
            }
        }
        return Iterables.filter(configurations, new Predicate<Configuration>() {
            public boolean apply(Configuration input) {
                return isMappedToIdeaScope(input, ideaModule);
            }
        });
    }

    private boolean isMappedToIdeaScope(final Configuration configuration, IdeaModule ideaModule) {
        Iterable<IdeaScopeMappingRule> rules = Iterables.concat(scopeMappings.values());
        boolean matchesRule = Iterables.any(rules, new Predicate<IdeaScopeMappingRule>() {
            public boolean apply(IdeaScopeMappingRule ideaScopeMappingRule) {
                return ideaScopeMappingRule.configurationNames.contains(configuration.getName());
            }
        });
        if (matchesRule) {
            return true;
        }
        for (Map<String, Collection<Configuration>> scopeMap : ideaModule.getScopes().values()) {
            Iterable<Configuration> configurations = Iterables.concat(scopeMap.values());
            if (Iterables.any(configurations, Predicates.equalTo(configuration))) {
                return true;
            }
        }
        return false;
    }

    /** Looks for dependencies contained in all configurations to remove them from multimap and return as result. */
    List<IdeDependencyKey<?, Dependency>> extractDependencies(
            Multimap<IdeDependencyKey<?, Dependency>, String> dependenciesToConfigs,
            Collection<String> configurations, Collection<String> minusConfigurations) {
        List<IdeDependencyKey<?, Dependency>> deps = new ArrayList<IdeDependencyKey<?, Dependency>>();
        List<IdeDependencyKey<?, Dependency>> minusDeps = new ArrayList<IdeDependencyKey<?, Dependency>>();
        for (IdeDependencyKey<?, Dependency> dependencyKey : dependenciesToConfigs.keySet()) {
            if (dependenciesToConfigs.get(dependencyKey).containsAll(configurations)) {
                boolean isInMinus = false;
                for (String minusConfiguration : minusConfigurations) {
                    if (dependenciesToConfigs.get(dependencyKey).contains(minusConfiguration)) {
                        isInMinus = true;
                        break;
                    }
                }
                if (!isInMinus) {
                    deps.add(dependencyKey);
                } else {
                    minusDeps.add(dependencyKey);
                }
            }
        }
        for (IdeDependencyKey<?, Dependency> key : Iterables.concat(deps, minusDeps)) {
            dependenciesToConfigs.removeAll(key);
        }
        return deps;
    }
}