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

org.jetbrains.android.facet.IdeaSourceProvider Maven / Gradle / Ivy

/*
 * Copyright (C) 2013 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 org.jetbrains.android.facet;

import com.android.builder.model.*;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.containers.HashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

/**
 * Like {@link SourceProvider}, but for IntelliJ, which means it provides
 * {@link VirtualFile} references rather than {@link File} references.
 *
 * @see org.jetbrains.android.facet.AndroidSourceType
 */
public abstract class IdeaSourceProvider {
  private IdeaSourceProvider() {
  }

  @NotNull
  public static IdeaSourceProvider create(@NotNull SourceProvider provider) {
    return new IdeaSourceProvider.Gradle(provider);
  }

  @NotNull
  public static IdeaSourceProvider create(@NotNull final AndroidFacet facet) {
    return new IdeaSourceProvider.Legacy(facet);
  }

  @NotNull
  public abstract String getName();

  @Nullable
  public abstract VirtualFile getManifestFile();

  @NotNull
  public abstract Collection getJavaDirectories();

  @NotNull
  public abstract Collection getResourcesDirectories();

  @NotNull
  public abstract Collection getAidlDirectories();

  @NotNull
  public abstract Collection getRenderscriptDirectories();

  @NotNull
  public abstract Collection getCDirectories();

  @NotNull
  public abstract Collection getCppDirectories();

  @NotNull
  public abstract Collection getJniLibsDirectories();

  @NotNull
  public abstract Collection getResDirectories();

  @NotNull
  public abstract Collection getAssetsDirectories();

  /** {@linkplain IdeaSourceProvider} for a Gradle projects */
  private static class Gradle extends IdeaSourceProvider {
    private final SourceProvider myProvider;
    private VirtualFile myManifestFile;
    private File myManifestIoFile;

    private Gradle(@NotNull SourceProvider provider) {
      myProvider = provider;
    }

    @NotNull
    @Override
    public String getName() {
      return myProvider.getName();
    }

    @Nullable
    @Override
    public VirtualFile getManifestFile() {
      File manifestFile = myProvider.getManifestFile();
      if (myManifestFile == null || !FileUtil.filesEqual(manifestFile, myManifestIoFile)) {
        myManifestIoFile = manifestFile;
        myManifestFile = LocalFileSystem.getInstance().findFileByIoFile(manifestFile);
      }

      return myManifestFile;
    }

    /** Convert a set of IO files into a set of equivalent virtual files */
    private static Collection convertFileSet(@NotNull Collection fileSet) {
      Collection result = Sets.newHashSetWithExpectedSize(fileSet.size());
      LocalFileSystem fileSystem = LocalFileSystem.getInstance();
      for (File file : fileSet) {
        VirtualFile virtualFile = fileSystem.findFileByIoFile(file);
        if (virtualFile != null) {
          result.add(virtualFile);
        }
      }
      return result;
    }

    @NotNull
    @Override
    public Collection getJavaDirectories() {
      return convertFileSet(myProvider.getJavaDirectories());
    }

    @NotNull
    @Override
    public Collection getResourcesDirectories() {
      return convertFileSet(myProvider.getResourcesDirectories());
    }

    @NotNull
    @Override
    public Collection getAidlDirectories() {
      return convertFileSet(myProvider.getAidlDirectories());
    }

    @NotNull
    @Override
    public Collection getRenderscriptDirectories() {
      return convertFileSet(myProvider.getRenderscriptDirectories());
    }

    @NotNull
    @Override
    public Collection getCDirectories() {
      return convertFileSet(myProvider.getCDirectories());
    }

    @NotNull
    @Override
    public Collection getCppDirectories() {
      return convertFileSet(myProvider.getCppDirectories());
    }

    @NotNull
    @Override
    public Collection getJniLibsDirectories() {
      return convertFileSet(myProvider.getJniLibsDirectories());
    }

    @NotNull
    @Override
    public Collection getResDirectories() {
      // TODO: Perform some caching; this method gets called a lot!
      return convertFileSet(myProvider.getResDirectories());
    }

    @NotNull
    @Override
    public Collection getAssetsDirectories() {
      return convertFileSet(myProvider.getAssetsDirectories());
    }

    /**
     * Compares another source provider with this for equality. Returns true if the specified object is also a Gradle source provider,
     * has the same name, and the same set of source locations.
     */
    @Override
    public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;

      Gradle that = (Gradle)o;
      if (!myProvider.getName().equals(that.getName())) return false;
      if (!myProvider.getManifestFile().getPath().equals(that.myProvider.getManifestFile().getPath())) return false;

      return true;
    }

    /**
     * Returns the hash code for this source provider. The hash code simply provides the hash of the manifest file's location,
     * but this follows the required contract that if two source providers are equal, their hash codes will be the same.
     */
    @Override
    public int hashCode() {
      return myProvider.getManifestFile().getPath().hashCode();
    }
  }

  /** {@linkplain IdeaSourceProvider} for a legacy (non-Gradle) Android project */
  private static class Legacy extends IdeaSourceProvider {
    @NotNull private final AndroidFacet myFacet;

    private Legacy(@NotNull AndroidFacet facet) {
      myFacet = facet;
    }

    @NotNull
    @Override
    public String getName() {
      return "";
    }

    @Nullable
    @Override
    public VirtualFile getManifestFile() {
      return AndroidRootUtil.getFileByRelativeModulePath(myFacet.getModule(), myFacet.getProperties().MANIFEST_FILE_RELATIVE_PATH, true);
    }

    @NotNull
    @Override
    public Collection getJavaDirectories() {
      Module module = myFacet.getModule();
      Collection dirs = new HashSet();
      Collections.addAll(dirs, ModuleRootManager.getInstance(module).getContentRoots());
      return dirs;
    }

    @NotNull
    @Override
    public Collection getResourcesDirectories() {
      return Collections.emptySet();
    }

    @NotNull
    @Override
    public Collection getAidlDirectories() {
      final VirtualFile dir = AndroidRootUtil.getAidlGenDir(myFacet);
      assert dir != null;
      return Collections.singleton(dir);
    }

    @NotNull
    @Override
    public Collection getRenderscriptDirectories() {
      final VirtualFile dir = AndroidRootUtil.getRenderscriptGenDir(myFacet);
      assert dir != null;
      return Collections.singleton(dir);
    }

    @NotNull
    @Override
    public Collection getCDirectories() {
     return Collections.emptySet();
    }

    @NotNull
    @Override
    public Collection getCppDirectories() {
      return Collections.emptySet();
    }

    @NotNull
    @Override
    public Collection getJniLibsDirectories() {
      return Collections.emptySet();
    }

    @NotNull
    @Override
    public Collection getResDirectories() {
      String resRelPath = myFacet.getProperties().RES_FOLDER_RELATIVE_PATH;
      final VirtualFile dir =  AndroidRootUtil.getFileByRelativeModulePath(myFacet.getModule(), resRelPath, true);
      if (dir != null) {
        return Collections.singleton(dir);
      } else {
        return Collections.emptySet();
      }
    }

    @NotNull
    @Override
    public Collection getAssetsDirectories() {
      final VirtualFile dir = AndroidRootUtil.getAssetsDir(myFacet);
      assert dir != null;
      return Collections.singleton(dir);
    }

    @Override
    public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;

      Legacy that = (Legacy)o;
      return myFacet.equals(that.myFacet);
    }

    @Override
    public int hashCode() {
      return myFacet.hashCode();
    }

  }

  /**
   * Returns a list of source providers, in the overlay order (meaning that later providers
   * override earlier providers when they redefine resources) for the currently selected variant.
   * 

* Note that the list will never be empty; there is always at least one source provider. *

* The overlay source order is defined by the Android Gradle plugin. */ @NotNull public static List getCurrentSourceProviders(@NotNull AndroidFacet facet) { if (!facet.isGradleProject()) { return Collections.singletonList(facet.getMainIdeaSourceProvider()); } List providers = Lists.newArrayList(); providers.add(facet.getMainIdeaSourceProvider()); List flavorSourceProviders = facet.getIdeaFlavorSourceProviders(); if (flavorSourceProviders != null) { for (IdeaSourceProvider provider : flavorSourceProviders) { providers.add(provider); } } IdeaSourceProvider multiProvider = facet.getIdeaMultiFlavorSourceProvider(); if (multiProvider != null) { providers.add(multiProvider); } IdeaSourceProvider buildTypeSourceProvider = facet.getIdeaBuildTypeSourceProvider(); if (buildTypeSourceProvider != null) { providers.add(buildTypeSourceProvider); } IdeaSourceProvider variantProvider = facet.getIdeaVariantSourceProvider(); if (variantProvider != null) { providers.add(variantProvider); } return providers; } @NotNull public static List getCurrentTestSourceProviders(@NotNull AndroidFacet facet) { if (!facet.isGradleProject()) { return Collections.emptyList(); } List providers = Lists.newArrayList(); providers.addAll(facet.getMainIdeaTestSourceProviders()); providers.addAll(facet.getIdeaFlavorTestSourceProviders()); //TODO: Does this make sense? //providers.addAll(facet.getIdeaMultiFlavorTestSourceProviders()); providers.addAll(facet.getIdeaBuildTypeTestSourceProvider()); //TODO: Does this make sense? //providers.addAll(facet.getIdeaVariantTestSourceProvider()); return providers; } private Collection getAllSourceFolders() { List srcDirectories = Lists.newArrayList(); srcDirectories.addAll(getJavaDirectories()); srcDirectories.addAll(getResDirectories()); srcDirectories.addAll(getAidlDirectories()); srcDirectories.addAll(getRenderscriptDirectories()); srcDirectories.addAll(getAssetsDirectories()); srcDirectories.addAll(getCDirectories()); srcDirectories.addAll(getCppDirectories()); srcDirectories.addAll(getJniLibsDirectories()); return srcDirectories; } private static Collection getAllSourceFolders(SourceProvider provider) { List srcDirectories = Lists.newArrayList(); srcDirectories.addAll(provider.getJavaDirectories()); srcDirectories.addAll(provider.getResDirectories()); srcDirectories.addAll(provider.getAidlDirectories()); srcDirectories.addAll(provider.getRenderscriptDirectories()); srcDirectories.addAll(provider.getAssetsDirectories()); srcDirectories.addAll(provider.getCDirectories()); srcDirectories.addAll(provider.getCppDirectories()); srcDirectories.addAll(provider.getJniLibsDirectories()); return srcDirectories; } /** * Returns true iff this SourceProvider provides the source folder that contains the given file. */ public boolean containsFile(@NotNull VirtualFile file) { Collection srcDirectories = getAllSourceFolders(); if (file.equals(getManifestFile())) { return true; } for (VirtualFile container : srcDirectories) { if (!container.exists()) { continue; } if (VfsUtilCore.isAncestor(container, file, false /* allow them to be the same */)) { return true; } // Check the flavor root directories if (file.equals(container.getParent())) { return true; } } return false; } /** * Returns true if this SourceProvider has one or more source folders contained by (or equal to) * the given folder. */ public static boolean isContainedBy(@NotNull SourceProvider provider, @NotNull File targetFolder) { Collection srcDirectories = getAllSourceFolders(provider); for (File container : srcDirectories) { if (FileUtil.isAncestor(targetFolder, container, false)) { return true; } if (!container.exists()) { continue; } if (VfsUtilCore.isAncestor(targetFolder, container, false /* allow them to be the same */)) { return true; } } return false; } /** * Returns true iff this SourceProvider provides the source folder that contains the given file. */ public static boolean containsFile(@NotNull SourceProvider provider, @NotNull File file) { Collection srcDirectories = getAllSourceFolders(provider); if (FileUtil.filesEqual(provider.getManifestFile(), file)) { return true; } for (File container : srcDirectories) { // Check the flavor root directories File parent = container.getParentFile(); if (parent != null && parent.isDirectory() && FileUtil.filesEqual(parent, file)) { return true; } // Don't do ancestry checking if this file doesn't exist if (!container.exists()) { continue; } if (VfsUtilCore.isAncestor(container, file, false /* allow them to be the same */)) { return true; } } return false; } /** * Returns true if this SourceProvider has one or more source folders contained by (or equal to) * the given folder. */ public boolean isContainedBy(@NotNull VirtualFile targetFolder) { Collection srcDirectories = getAllSourceFolders(); for (VirtualFile container : srcDirectories) { if (!container.exists()) { continue; } if (VfsUtilCore.isAncestor(targetFolder, container, false /* allow them to be the same */)) { return true; } } return false; } /** * Returns an iterable of all source providers, for the given facet, * in the overlay order (meaning that later providers * override earlier providers when they redefine resources.) *

* Note that the list will never be empty; there is always at least one source provider. *

* The overlay source order is defined by the Android Gradle plugin. */ @NotNull public static List getAllSourceProviders(@NotNull AndroidFacet facet) { if (!facet.isGradleProject() || facet.getIdeaAndroidProject() == null) { return Collections.singletonList(facet.getMainSourceProvider()); } AndroidProject androidProject = facet.getIdeaAndroidProject().getDelegate(); Collection variants = androidProject.getVariants(); List providers = Lists.newArrayList(); // Add main source set providers.add(facet.getMainSourceProvider()); // Add all flavors Collection flavors = androidProject.getProductFlavors(); for (ProductFlavorContainer pfc : flavors) { providers.add(pfc.getSourceProvider()); } // Add the multi-flavor source providers for (Variant v : variants) { SourceProvider provider = v.getMainArtifact().getMultiFlavorSourceProvider(); if (provider != null) { providers.add(provider); } } // Add all the build types Collection buildTypes = androidProject.getBuildTypes(); for (BuildTypeContainer btc : buildTypes) { providers.add(btc.getSourceProvider()); } // Add all the variant source providers for (Variant v : variants) { SourceProvider provider = v.getMainArtifact().getVariantSourceProvider(); if (provider != null) { providers.add(provider); } } return providers; } /** * Returns a list of all IDEA source providers, for the given facet, in the overlay order * (meaning that later providers override earlier providers when they redefine resources.) *

* Note that the list will never be empty; there is always at least one source provider. *

* The overlay source order is defined by the Android Gradle plugin. * * This method should be used when only on-disk source sets are required. It will return * empty source sets for all other source providers (since VirtualFiles MUST exist on disk). */ @NotNull public static List getAllIdeaSourceProviders(@NotNull AndroidFacet facet) { List ideaSourceProviders = Lists.newArrayList(); for (SourceProvider sourceProvider : getAllSourceProviders(facet)) { ideaSourceProviders.add(create(sourceProvider)); } return ideaSourceProviders; } /** * Returns a list of all IDEA source providers that contain, or are contained by, the given file. * For example, with the file structure: *

   * src
   *   main
   *     aidl
   *       myfile.aidl
   *   free
   *     aidl
   *       myoverlay.aidl
   * 
* * With target file == "myoverlay.aidl" the returned list would be ['free'], but if target file == "src", * the returned list would be ['main', 'free'] since both of those source providers have source folders which * are descendants of "src." */ @NotNull public static List getIdeaSourceProvidersForFile(@NotNull AndroidFacet facet, @Nullable VirtualFile targetFolder, @Nullable IdeaSourceProvider defaultIdeaSourceProvider) { List sourceProviderList = Lists.newArrayList(); if (targetFolder != null) { // Add source providers that contain the file (if any) and any that have files under the given folder for (IdeaSourceProvider provider : getAllIdeaSourceProviders(facet)) { if (provider.containsFile(targetFolder) || provider.isContainedBy(targetFolder)) { sourceProviderList.add(provider); } } } if (sourceProviderList.isEmpty() && defaultIdeaSourceProvider != null) { sourceProviderList.add(defaultIdeaSourceProvider); } return sourceProviderList; } /** * Returns a list of all source providers that contain, or are contained by, the given file. * For example, with the file structure: *
   * src
   *   main
   *     aidl
   *       myfile.aidl
   *   free
   *     aidl
   *       myoverlay.aidl
   * 
* * With target file == "myoverlay.aidl" the returned list would be ['free'], but if target file == "src", * the returned list would be ['main', 'free'] since both of those source providers have source folders which * are descendants of "src." */ @NotNull public static List getSourceProvidersForFile(@NotNull AndroidFacet facet, @Nullable VirtualFile targetFolder, @Nullable SourceProvider defaultSourceProvider) { List sourceProviderList = Lists.newArrayList(); if (targetFolder != null) { File targetIoFolder = VfsUtilCore.virtualToIoFile(targetFolder); // Add source providers that contain the file (if any) and any that have files under the given folder for (SourceProvider provider : getAllSourceProviders(facet)) { if (containsFile(provider, targetIoFolder) || isContainedBy(provider, targetIoFolder)) { sourceProviderList.add(provider); } } } if (sourceProviderList.isEmpty() && defaultSourceProvider != null) { sourceProviderList.add(defaultSourceProvider); } return sourceProviderList; } /** Returns true if the given candidate file is a manifest file in the given module */ public static boolean isManifestFile(@NotNull AndroidFacet facet, @Nullable VirtualFile candidate) { if (candidate == null) { return false; } if (facet.isGradleProject()) { for (IdeaSourceProvider provider : getCurrentSourceProviders(facet)) { if (candidate.equals(provider.getManifestFile())) { return true; } } return false; } else { return candidate.equals(facet.getMainIdeaSourceProvider().getManifestFile()); } } /** Returns the manifest files in the given module */ @NotNull public static List getManifestFiles(@NotNull AndroidFacet facet) { VirtualFile main = facet.getMainIdeaSourceProvider().getManifestFile(); if (!facet.isGradleProject()) { return main != null ? Collections.singletonList(main) : Collections.emptyList(); } List files = Lists.newArrayList(); if (main != null) { files.add(main); } for (IdeaSourceProvider provider : getCurrentSourceProviders(facet)) { VirtualFile manifest = provider.getManifestFile(); if (manifest != null) { files.add(manifest); } } return files; } public static Function> MANIFEST_PROVIDER = new Function>() { @Override public List apply(IdeaSourceProvider provider) { VirtualFile manifestFile = provider.getManifestFile(); return manifestFile == null ? Collections.emptyList() : Collections.singletonList(manifestFile); } }; public static Function> RES_PROVIDER = new Function>() { @Override public List apply(IdeaSourceProvider provider) { return Lists.newArrayList(provider.getResDirectories()); } }; public static Function> JAVA_PROVIDER = new Function>() { @Override public List apply(IdeaSourceProvider provider) { return Lists.newArrayList(provider.getJavaDirectories()); } }; public static Function> RESOURCES_PROVIDER = new Function>() { @Override public List apply(IdeaSourceProvider provider) { return Lists.newArrayList(provider.getResourcesDirectories()); } }; public static Function> AIDL_PROVIDER = new Function>() { @Override public List apply(IdeaSourceProvider provider) { return Lists.newArrayList(provider.getAidlDirectories()); } }; public static Function> C_PROVIDER = new Function>() { @Override public List apply(IdeaSourceProvider provider) { return Lists.newArrayList(provider.getCDirectories()); } }; public static Function> CPP_PROVIDER = new Function>() { @Override public List apply(IdeaSourceProvider provider) { return Lists.newArrayList(provider.getCppDirectories()); } }; public static Function> JNI_LIBS_PROVIDER = new Function>() { @Override public List apply(IdeaSourceProvider provider) { return Lists.newArrayList(provider.getJniLibsDirectories()); } }; public static Function> ASSETS_PROVIDER = new Function>() { @Override public List apply(IdeaSourceProvider provider) { return Lists.newArrayList(provider.getAssetsDirectories()); } }; public static Function> RS_PROVIDER = new Function>() { @Override public List apply(IdeaSourceProvider provider) { return Lists.newArrayList(provider.getRenderscriptDirectories()); } }; }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy