All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.gradle.plugins.ide.idea.model.internal.IdeaDependenciesProvider Maven / Gradle / Ivy

There is a newer version: 8.11.1
Show newest version
/*
 * 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.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.component.ComponentSelector;
import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
import org.gradle.api.artifacts.dsl.DependencyHandler;
import org.gradle.api.artifacts.result.ResolvedArtifactResult;
import org.gradle.api.artifacts.result.UnresolvedDependencyResult;
import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier;
import org.gradle.api.internal.project.ProjectInternal;
import org.gradle.api.internal.project.ProjectStateRegistry;
import org.gradle.internal.Factory;
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.Path;
import org.gradle.plugins.ide.idea.model.SingleEntryModuleLibrary;
import org.gradle.plugins.ide.internal.IdeArtifactRegistry;
import org.gradle.plugins.ide.internal.resolver.IdeDependencySet;
import org.gradle.plugins.ide.internal.resolver.IdeDependencyVisitor;
import org.gradle.plugins.ide.internal.resolver.UnresolvedIdeDependencyHandler;

import javax.annotation.Nullable;
import java.io.File;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class IdeaDependenciesProvider {

    public static final String SCOPE_PLUS = "plus";
    public static final String SCOPE_MINUS = "minus";
    private final ModuleDependencyBuilder moduleDependencyBuilder;
    private final IdeaDependenciesOptimizer optimizer;
    private final ProjectComponentIdentifier currentProjectId;

    public IdeaDependenciesProvider(Project project, IdeArtifactRegistry artifactRegistry, ProjectStateRegistry projectRegistry) {
        moduleDependencyBuilder = new ModuleDependencyBuilder(artifactRegistry);
        currentProjectId = projectRegistry.stateFor(project).getComponentIdentifier();
        optimizer = new IdeaDependenciesOptimizer();
    }

    public Set provide(final IdeaModule ideaModule) {
        Set result = Sets.newLinkedHashSet();
        result.addAll(getOutputLocations(ideaModule));
        result.addAll(getDependencies(ideaModule));
        return result;
    }

    private Set getOutputLocations(IdeaModule ideaModule) {
        if (ideaModule.getSingleEntryLibraries() == null) {
            return Collections.emptySet();
        }
        Set outputLocations = Sets.newLinkedHashSet();
        for (Map.Entry> outputLocation : ideaModule.getSingleEntryLibraries().entrySet()) {
            String scope = outputLocation.getKey();
            for (File file : outputLocation.getValue()) {
                if (file != null && file.isDirectory()) {
                    outputLocations.add(new SingleEntryModuleLibrary(toPath(ideaModule, file), scope));
                }
            }
        }
        return outputLocations;
    }

    private Set getDependencies(IdeaModule ideaModule) {
        Set dependencies = Sets.newLinkedHashSet();
        Map unresolvedDependencies = Maps.newLinkedHashMap();
        for (GeneratedIdeaScope scope : GeneratedIdeaScope.values()) {
            IdeaDependenciesVisitor visitor = visitDependencies(ideaModule, scope);
            dependencies.addAll(visitor.getDependencies());
            unresolvedDependencies.putAll(visitor.getUnresolvedDependencies());
        }
        optimizer.optimizeDeps(dependencies);
        new UnresolvedIdeDependencyHandler().log(unresolvedDependencies.values());
        return dependencies;
    }

    private IdeaDependenciesVisitor visitDependencies(IdeaModule ideaModule, GeneratedIdeaScope scope) {
        ProjectInternal projectInternal = (ProjectInternal) ideaModule.getProject();
        final DependencyHandler handler = projectInternal.getDependencies();
        final Collection plusConfigurations = getPlusConfigurations(ideaModule, scope);
        final Collection minusConfigurations = getMinusConfigurations(ideaModule, scope);

        final IdeaDependenciesVisitor visitor = new IdeaDependenciesVisitor(ideaModule, scope.name());
        return projectInternal.getMutationState().withMutableState(new Factory() {
            @Nullable
            @Override
            public IdeaDependenciesVisitor create() {
                new IdeDependencySet(handler, plusConfigurations, minusConfigurations).visit(visitor);
                return visitor;
            }
        });

    }

    private Collection getPlusConfigurations(IdeaModule ideaModule, GeneratedIdeaScope scope) {
        return getConfigurations(ideaModule, scope, SCOPE_PLUS);
    }

    private Collection getMinusConfigurations(IdeaModule ideaModule, GeneratedIdeaScope scope) {
        return getConfigurations(ideaModule, scope, SCOPE_MINUS);
    }

    private Collection getConfigurations(IdeaModule ideaModule, GeneratedIdeaScope scope, String plusMinus) {
        Map> plusMinusConfigurations = getPlusMinusConfigurations(ideaModule, scope);
        return plusMinusConfigurations.containsKey(plusMinus) ? plusMinusConfigurations.get(plusMinus) : Collections.emptyList();
    }

    private Map> getPlusMinusConfigurations(IdeaModule ideaModule, GeneratedIdeaScope scope) {
        Map> plusMinusConfigurations = ideaModule.getScopes().get(scope.name());
        return plusMinusConfigurations != null ? plusMinusConfigurations : Collections.>emptyMap();
    }

    private FilePath toPath(IdeaModule ideaModule, File file) {
        return file != null ? ideaModule.getPathFactory().path(file) : null;
    }

    private class IdeaDependenciesVisitor implements IdeDependencyVisitor {
        private final IdeaModule ideaModule;
        private final UnresolvedIdeDependencyHandler unresolvedIdeDependencyHandler = new UnresolvedIdeDependencyHandler();
        private final String scope;

        private final List projectDependencies = Lists.newLinkedList();
        private final List moduleDependencies = Lists.newLinkedList();
        private final List fileDependencies = Lists.newLinkedList();
        private final Map unresolvedDependencies = Maps.newLinkedHashMap();

        private IdeaDependenciesVisitor(IdeaModule ideaModule, String scope) {
            this.ideaModule = ideaModule;
            this.scope = scope;
        }

        @Override
        public boolean isOffline() {
            return ideaModule.isOffline();
        }

        @Override
        public boolean downloadSources() {
            return ideaModule.isDownloadSources();
        }

        @Override
        public boolean downloadJavaDoc() {
            return ideaModule.isDownloadJavadoc();
        }

        @Override
        public void visitProjectDependency(ResolvedArtifactResult artifact) {
            ProjectComponentIdentifier projectId = (ProjectComponentIdentifier) artifact.getId().getComponentIdentifier();
            if (!projectId.equals(currentProjectId)) {
                projectDependencies.add(moduleDependencyBuilder.create(projectId, scope));
            }
        }

        @Override
        public void visitModuleDependency(ResolvedArtifactResult artifact, Set sources, Set javaDoc) {
            ModuleComponentIdentifier moduleId = (ModuleComponentIdentifier) artifact.getId().getComponentIdentifier();
            SingleEntryModuleLibrary library = new SingleEntryModuleLibrary(toPath(ideaModule, artifact.getFile()), scope);
            library.setModuleVersion(DefaultModuleVersionIdentifier.newId(moduleId.getModuleIdentifier(), moduleId.getVersion()));
            Set sourcePaths = Sets.newLinkedHashSet();
            for (ResolvedArtifactResult sourceArtifact : sources) {
                sourcePaths.add(toPath(ideaModule, sourceArtifact.getFile()));
            }
            library.setSources(sourcePaths);
            Set javaDocPaths = Sets.newLinkedHashSet();
            for (ResolvedArtifactResult javaDocArtifact : javaDoc) {
                javaDocPaths.add(toPath(ideaModule, javaDocArtifact.getFile()));
            }
            library.setJavadoc(javaDocPaths);
            moduleDependencies.add(library);
        }

        @Override
        public void visitFileDependency(ResolvedArtifactResult artifact) {
            fileDependencies.add(new SingleEntryModuleLibrary(toPath(ideaModule, artifact.getFile()), scope));
        }

        /*
         * Remembers the unresolved dependency for later logging and also adds a fake
         * file dependency, with the file path pointing to the attempted component selector.
         * This shows up in the IDE as a red flag in the dependencies view. That's not the best
         * usability and it also muddies the API contract, because we disguise an unresolved
         * dependency as a file dependency, even though that file really doesn't exist.
         *
         * Instead, when generating files on the command line, the logged warning is enough.
         * When using the Tooling API, a dedicated "unresolved dependency" object would be better
         * and could be shown in a notification. The command line warning should probably be omitted in that case.
         */
        @Override
        public void visitUnresolvedDependency(UnresolvedDependencyResult unresolvedDependency) {
            File unresolvedFile = unresolvedIdeDependencyHandler.asFile(unresolvedDependency, ideaModule.getContentRoot());
            fileDependencies.add(new SingleEntryModuleLibrary(toPath(ideaModule, unresolvedFile), scope));
            unresolvedDependencies.put(unresolvedDependency.getAttempted(), unresolvedDependency);
        }

        /*
         * This method returns the dependencies in buckets (projects first, then modules, then files),
         * because that's what we used to do since 1.0. It would be better to return the dependencies
         * in the same order as they come from the resolver, but we'll need to change all the tests for
         * that, so defer that until later.
         */
        public Collection getDependencies() {
            Collection dependencies = Sets.newLinkedHashSet();
            dependencies.addAll(projectDependencies);
            dependencies.addAll(moduleDependencies);
            dependencies.addAll(fileDependencies);
            return dependencies;
        }

        public Map getUnresolvedDependencies() {
            return unresolvedDependencies;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy