/* * Copyright (C) 2014 The Android Open Source Project * * 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 com.android.tools.idea.gradle.customizer.java; import com.android.tools.idea.gradle.IdeaJavaProject; import com.android.tools.idea.gradle.JavaModel; import com.android.tools.idea.gradle.customizer.AbstractDependenciesModuleCustomizer; import com.android.tools.idea.gradle.facet.JavaGradleFacet; import com.android.tools.idea.gradle.messages.Message; import com.android.tools.idea.gradle.messages.ProjectSyncMessages; import com.android.tools.idea.gradle.util.Projects; import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.intellij.facet.FacetManager; import com.intellij.facet.ModifiableFacetModel; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.roots.DependencyScope; import com.intellij.openapi.roots.ModifiableRootModel; import com.intellij.openapi.roots.ModuleOrderEntry; import com.intellij.openapi.util.io.FileUtil; import org.gradle.tooling.model.idea.*; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; import java.util.Collections; import java.util.List; import static com.android.tools.idea.gradle.messages.CommonMessageGroupNames.FAILED_TO_SET_UP_DEPENDENCIES; import static com.intellij.openapi.util.io.FileUtil.getNameWithoutExtension; import static com.intellij.openapi.util.io.FileUtil.sanitizeFileName; import static java.util.Collections.singletonList; public class DependenciesModuleCustomizer extends AbstractDependenciesModuleCustomizer<IdeaJavaProject> { @NotNull @NonNls private static final String UNRESOLVED_DEPENDENCY_PREFIX = "unresolved dependency - "; private static final Logger LOG = Logger.getInstance(AbstractDependenciesModuleCustomizer.class); private static final DependencyScope DEFAULT_DEPENDENCY_SCOPE = DependencyScope.COMPILE; @Override protected void setUpDependencies(@NotNull ModifiableRootModel model, @NotNull IdeaJavaProject javaProject, @NotNull List<Message> errorsFound) { List<String> unresolved = Lists.newArrayList(); List<? extends IdeaDependency> dependencies = javaProject.getDependencies(); for (IdeaDependency dependency : dependencies) { if (dependency instanceof IdeaModuleDependency) { updateDependency(model, (IdeaModuleDependency)dependency, errorsFound); continue; } if (dependency instanceof IdeaSingleEntryLibraryDependency) { IdeaSingleEntryLibraryDependency libDependency = (IdeaSingleEntryLibraryDependency)dependency; if (isResolved(libDependency)) { updateDependency(model, (IdeaSingleEntryLibraryDependency)dependency, errorsFound); continue; } String name = getUnresolvedDependencyName(libDependency); if (name != null) { unresolved.add(name); } } } Module module = model.getModule(); ProjectSyncMessages messages = ProjectSyncMessages.getInstance(model.getProject()); messages.reportUnresolvedDependencies(unresolved, module); JavaGradleFacet facet = setAndGetJavaGradleFacet(module); File buildFolderPath = javaProject.getBuildFolderPath(); if (Projects.isGradleProjectModule(module)) { // For project module we store the path of "build" folder in the facet itself, so the caching mechanism can obtain the path when // the project is opened. This module does not need a JavaModel. facet.getConfiguration().BUILD_FOLDER_PATH = buildFolderPath != null ? FileUtil.toSystemIndependentName(buildFolderPath.getPath()) : ""; } else { JavaModel javaModel = new JavaModel(unresolved, buildFolderPath); facet.setJavaModel(javaModel); } } private static boolean isResolved(@NotNull IdeaSingleEntryLibraryDependency dependency) { String libraryName = getFileName(dependency); return libraryName != null && !libraryName.startsWith(UNRESOLVED_DEPENDENCY_PREFIX); } @Nullable private static String getUnresolvedDependencyName(@NotNull IdeaSingleEntryLibraryDependency dependency) { String libraryName = getFileName(dependency); if (libraryName == null) { return null; } // Gradle uses names like 'unresolved dependency - commons-collections commons-collections 3.2' for unresolved dependencies. // We report the unresolved dependency as 'commons-collections:commons-collections:3.2' return libraryName.substring(UNRESOLVED_DEPENDENCY_PREFIX.length()).replace(' ', ':'); } @Nullable private static String getFileName(@NotNull IdeaSingleEntryLibraryDependency dependency) { File binaryPath = dependency.getFile(); return binaryPath != null ? binaryPath.getName() : null; } private static void updateDependency(@NotNull ModifiableRootModel model, @NotNull IdeaModuleDependency dependency, @NotNull List<Message> errorsFound) { IdeaModule dependencyModule = dependency.getDependencyModule(); if (dependencyModule == null || Strings.isNullOrEmpty(dependencyModule.getName())) { String msg = "Found a module dependency without name: " + dependency; LOG.info(msg); errorsFound.add(new Message(FAILED_TO_SET_UP_DEPENDENCIES, Message.Type.ERROR, msg)); return; } String moduleName = dependencyModule.getName(); ModuleManager moduleManager = ModuleManager.getInstance(model.getProject()); Module found = null; for (Module module : moduleManager.getModules()) { if (moduleName.equals(module.getName())) { found = module; } } if (found != null) { ModuleOrderEntry orderEntry = model.addModuleOrderEntry(found); orderEntry.setExported(true); return; } String msg = String.format("Unable fo find module '%1$s'.", moduleName); LOG.info(msg); errorsFound.add(new Message(FAILED_TO_SET_UP_DEPENDENCIES, Message.Type.ERROR, msg)); } private void updateDependency(@NotNull ModifiableRootModel model, @NotNull IdeaSingleEntryLibraryDependency dependency, @NotNull List<Message> errorsFound) { DependencyScope scope = parseScope(dependency.getScope()); File binaryPath = dependency.getFile(); if (binaryPath == null) { String msg = "Found a library dependency without a 'binary' path: " + dependency; LOG.info(msg); errorsFound.add(new Message(FAILED_TO_SET_UP_DEPENDENCIES, Message.Type.ERROR, msg)); return; } String path = binaryPath.getPath(); // Gradle API doesn't provide library name at the moment. String name = binaryPath.isFile() ? getNameWithoutExtension(binaryPath) : sanitizeFileName(path); setUpLibraryDependency(model, name, scope, singletonList(path), getPath(dependency.getSource()), getPath(dependency.getJavadoc())); } @NotNull private static List<String> getPath(@Nullable File file) { return file == null ? Collections.<String>emptyList() : singletonList(file.getPath()); } @NotNull private static DependencyScope parseScope(@Nullable IdeaDependencyScope scope) { if (scope == null) { return DEFAULT_DEPENDENCY_SCOPE; } String description = scope.getScope(); if (description == null) { return DEFAULT_DEPENDENCY_SCOPE; } for (DependencyScope dependencyScope : DependencyScope.values()) { if (description.equalsIgnoreCase(dependencyScope.toString())) { return dependencyScope; } } return DEFAULT_DEPENDENCY_SCOPE; } @NotNull private static JavaGradleFacet setAndGetJavaGradleFacet(Module module) { JavaGradleFacet facet = JavaGradleFacet.getInstance(module); if (facet != null) { return facet; } // Module does not have Android-Gradle facet. Create one and add it. FacetManager facetManager = FacetManager.getInstance(module); ModifiableFacetModel model = facetManager.createModifiableModel(); try { facet = facetManager.createFacet(JavaGradleFacet.getFacetType(), JavaGradleFacet.NAME, null); model.addFacet(facet); } finally { model.commit(); } return facet; } }