/*
* Copyright 2000-2012 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 com.intellij.openapi.roots.impl;
import com.google.common.base.Predicate;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.roots.*;
import consulo.roots.impl.ModuleRootsProcessor;
import consulo.roots.types.BinariesOrderRootType;
import consulo.roots.types.SourcesOrderRootType;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.*;
import com.intellij.util.containers.ContainerUtil;
import consulo.compiler.ModuleCompilerPathsManager;
import consulo.util.NotNullPairFunction;
import org.jetbrains.annotations.NotNull;
import consulo.roots.ContentFolderScopes;
import consulo.roots.ContentFolderTypeProvider;
import java.util.*;
/**
* @author nik
*/
public class OrderRootsEnumeratorImpl implements OrderRootsEnumerator {
private static final NotNullPairFunction<ContentEntry, Predicate<ContentFolderTypeProvider>, VirtualFile[]> ourSourcesToFileFunc =
new NotNullPairFunction<ContentEntry, Predicate<ContentFolderTypeProvider>, VirtualFile[]>() {
@NotNull
@Override
public VirtualFile[] fun(ContentEntry t, Predicate<ContentFolderTypeProvider> v) {
return t.getFolderFiles(v);
}
};
private static final NotNullPairFunction<ContentEntry, Predicate<ContentFolderTypeProvider>, String[]> ourSourcesToUrlFunc =
new NotNullPairFunction<ContentEntry, Predicate<ContentFolderTypeProvider>, String[]>() {
@NotNull
@Override
public String[] fun(ContentEntry t, Predicate<ContentFolderTypeProvider> v) {
return t.getFolderUrls(v);
}
};
private static final NotNullPairFunction<ModuleRootModel, Predicate<ContentFolderTypeProvider>, VirtualFile[]> ourRuntimeToFileFunc =
new NotNullPairFunction<ModuleRootModel, Predicate<ContentFolderTypeProvider>, VirtualFile[]>() {
@NotNull
@Override
public VirtualFile[] fun(ModuleRootModel t, Predicate<ContentFolderTypeProvider> v) {
List<VirtualFile> files = new ArrayList<VirtualFile>(5);
ModuleCompilerPathsManager compilerPathsManager = ModuleCompilerPathsManager.getInstance(t.getModule());
for (ContentFolderTypeProvider contentFolderTypeProvider : ContentFolderTypeProvider.EP_NAME.getExtensions()) {
if(v.apply(contentFolderTypeProvider)) {
ContainerUtil.addIfNotNull(files, compilerPathsManager.getCompilerOutput(contentFolderTypeProvider));
}
}
return VfsUtilCore.toVirtualFileArray(files);
}
};
private static final NotNullPairFunction<ModuleRootModel, Predicate<ContentFolderTypeProvider>, String[]> ourRuntimeToUrlFunc =
new NotNullPairFunction<ModuleRootModel, Predicate<ContentFolderTypeProvider>, String[]>() {
@NotNull
@Override
public String[] fun(ModuleRootModel t, Predicate<ContentFolderTypeProvider> v) {
List<String> urls = new ArrayList<String>(5);
ModuleCompilerPathsManager compilerPathsManager = ModuleCompilerPathsManager.getInstance(t.getModule());
for (ContentFolderTypeProvider contentFolderTypeProvider : ContentFolderTypeProvider.EP_NAME.getExtensions()) {
if(v.apply(contentFolderTypeProvider)) {
ContainerUtil.addIfNotNull(urls, compilerPathsManager.getCompilerOutputUrl(contentFolderTypeProvider));
}
}
return ArrayUtil.toStringArray(urls);
}
};
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.roots.impl.OrderRootsEnumeratorImpl");
private final OrderEnumeratorBase myOrderEnumerator;
private final OrderRootType myRootType;
private final NotNullFunction<OrderEntry, OrderRootType> myRootTypeProvider;
private boolean myUsingCache;
private NotNullFunction<OrderEntry, VirtualFile[]> myCustomRootProvider;
private boolean myWithoutSelfModuleOutput;
public OrderRootsEnumeratorImpl(OrderEnumeratorBase orderEnumerator, @NotNull OrderRootType rootType) {
myOrderEnumerator = orderEnumerator;
myRootType = rootType;
myRootTypeProvider = null;
}
public OrderRootsEnumeratorImpl(OrderEnumeratorBase orderEnumerator,
@NotNull NotNullFunction<OrderEntry, OrderRootType> rootTypeProvider) {
myOrderEnumerator = orderEnumerator;
myRootTypeProvider = rootTypeProvider;
myRootType = null;
}
@NotNull
@Override
public VirtualFile[] getRoots() {
if (myUsingCache) {
checkCanUseCache();
final OrderRootsCache cache = myOrderEnumerator.getCache();
if (cache != null) {
final int flags = myOrderEnumerator.getFlags();
final VirtualFile[] cached = cache.getCachedRoots(myRootType, flags);
if (cached == null) {
return cache.setCachedRoots(myRootType, flags, computeRootsUrls()).getFiles();
}
else {
return cached;
}
}
}
return VfsUtilCore.toVirtualFileArray(computeRoots());
}
@NotNull
@Override
public String[] getUrls() {
if (myUsingCache) {
checkCanUseCache();
final OrderRootsCache cache = myOrderEnumerator.getCache();
if (cache != null) {
final int flags = myOrderEnumerator.getFlags();
String[] cached = cache.getCachedUrls(myRootType, flags);
if (cached == null) {
return cache.setCachedRoots(myRootType, flags, computeRootsUrls()).getUrls();
}
else {
return cached;
}
}
}
return ArrayUtil.toStringArray(computeRootsUrls());
}
private void checkCanUseCache() {
LOG.assertTrue(myRootTypeProvider == null, "Caching not supported for OrderRootsEnumerator with root type provider");
LOG.assertTrue(myCustomRootProvider == null, "Caching not supported for OrderRootsEnumerator with 'usingCustomRootProvider' option");
LOG.assertTrue(!myWithoutSelfModuleOutput, "Caching not supported for OrderRootsEnumerator with 'withoutSelfModuleOutput' option");
}
private Collection<VirtualFile> computeRoots() {
final Collection<VirtualFile> result = new LinkedHashSet<VirtualFile>();
myOrderEnumerator.forEach(new Processor<OrderEntry>() {
@Override
public boolean process(OrderEntry orderEntry) {
OrderRootType type = getRootType(orderEntry);
if (orderEntry instanceof ModuleSourceOrderEntry) {
collectRoots(type, ((ModuleSourceOrderEntry)orderEntry).getRootModel(), result, true, !myOrderEnumerator.isProductionOnly(),
ourSourcesToFileFunc, ourRuntimeToFileFunc);
}
else if (orderEntry instanceof ModuleOrderEntry) {
ModuleOrderEntry moduleOrderEntry = (ModuleOrderEntry)orderEntry;
final Module module = moduleOrderEntry.getModule();
if (module != null) {
ModuleRootModel rootModel = myOrderEnumerator.getRootModel(module);
boolean productionOnTests =
orderEntry instanceof ModuleOrderEntryImpl && ((ModuleOrderEntryImpl)orderEntry).isProductionOnTestDependency();
boolean includeTests =
!myOrderEnumerator.isProductionOnly() && myOrderEnumerator.shouldIncludeTestsFromDependentModulesToTestClasspath() ||
productionOnTests;
collectRoots(type, rootModel, result, !productionOnTests, includeTests, ourSourcesToFileFunc, ourRuntimeToFileFunc);
}
}
else {
if (myCustomRootProvider != null) {
Collections.addAll(result, myCustomRootProvider.fun(orderEntry));
return true;
}
if (myOrderEnumerator.addCustomRootsForLibrary(orderEntry, type, result)) {
return true;
}
Collections.addAll(result, orderEntry.getFiles(type));
}
return true;
}
});
return result;
}
@NotNull
private Collection<String> computeRootsUrls() {
final Collection<String> result = new LinkedHashSet<String>();
myOrderEnumerator.forEach(new Processor<OrderEntry>() {
@Override
public boolean process(OrderEntry orderEntry) {
OrderRootType type = getRootType(orderEntry);
if (orderEntry instanceof ModuleSourceOrderEntry) {
collectRootUrls(type, ((ModuleSourceOrderEntry)orderEntry).getRootModel(), result, true, !myOrderEnumerator.isProductionOnly(), ourSourcesToUrlFunc,
ourRuntimeToUrlFunc);
}
else if (orderEntry instanceof ModuleOrderEntry) {
ModuleOrderEntry moduleOrderEntry = (ModuleOrderEntry)orderEntry;
final Module module = moduleOrderEntry.getModule();
if (module != null) {
ModuleRootModel rootModel = myOrderEnumerator.getRootModel(module);
boolean productionOnTests =
orderEntry instanceof ModuleOrderEntryImpl && ((ModuleOrderEntryImpl)orderEntry).isProductionOnTestDependency();
boolean includeTests =
!myOrderEnumerator.isProductionOnly() && myOrderEnumerator.shouldIncludeTestsFromDependentModulesToTestClasspath() ||
productionOnTests;
collectRootUrls(type, rootModel, result, !productionOnTests, includeTests, ourSourcesToUrlFunc, ourRuntimeToUrlFunc);
}
}
else {
if (myOrderEnumerator.addCustomRootUrlsForLibrary(orderEntry, type, result)) {
return true;
}
Collections.addAll(result, orderEntry.getUrls(type));
}
return true;
}
});
return result;
}
@NotNull
@Override
public PathsList getPathsList() {
final PathsList list = new PathsList();
collectPaths(list);
return list;
}
@Override
public void collectPaths(@NotNull PathsList list) {
list.addVirtualFiles(getRoots());
}
@NotNull
@Override
public OrderRootsEnumerator usingCache() {
myUsingCache = true;
return this;
}
@NotNull
@Override
public OrderRootsEnumerator withoutSelfModuleOutput() {
myWithoutSelfModuleOutput = true;
return this;
}
@NotNull
@Override
public OrderRootsEnumerator usingCustomRootProvider(@NotNull NotNullFunction<OrderEntry, VirtualFile[]> provider) {
myCustomRootProvider = provider;
return this;
}
private void collectRoots(OrderRootType type,
ModuleRootModel rootModel,
Collection<VirtualFile> result,
final boolean includeProduction,
final boolean includeTests,
NotNullPairFunction<ContentEntry, Predicate<ContentFolderTypeProvider>, VirtualFile[]> funForSources,
NotNullPairFunction<ModuleRootModel, Predicate<ContentFolderTypeProvider>, VirtualFile[]> funForRuntime) {
ModuleRootsProcessor rootsProcessor = ModuleRootsProcessor.findRootsProcessor(rootModel);
if (type.equals(SourcesOrderRootType.getInstance())) {
if (includeProduction) {
Predicate<ContentFolderTypeProvider> predicate = includeTests ? ContentFolderScopes.productionAndTest() : ContentFolderScopes.production();
if(rootsProcessor != null) {
rootsProcessor.processFiles(rootModel, predicate, new CommonProcessors.CollectProcessor<VirtualFile>(result));
}
else {
for (ContentEntry entry : rootModel.getContentEntries()) {
Collections.addAll(result, funForSources.fun(entry, predicate));
}
}
}
else {
if(rootsProcessor != null) {
rootsProcessor.processFiles(rootModel, ContentFolderScopes.test(), new CommonProcessors.CollectProcessor<VirtualFile>(result));
}
else {
for (ContentEntry entry : rootModel.getContentEntries()) {
Collections.addAll(result, funForSources.fun(entry, ContentFolderScopes.test()));
}
}
}
}
else if (type.equals(BinariesOrderRootType.getInstance())) {
if (myWithoutSelfModuleOutput && myOrderEnumerator.isRootModuleModel(rootModel)) {
if (includeTests && includeProduction) {
Collections.addAll(result, funForRuntime.fun(rootModel, ContentFolderScopes.productionAndTest()));
}
}
else {
Collections.addAll(result, funForRuntime
.fun(rootModel, includeTests ? ContentFolderScopes.productionAndTest() : ContentFolderScopes.production()));
}
}
}
private void collectRootUrls(OrderRootType type,
ModuleRootModel rootModel,
Collection<String> result,
final boolean includeProduction,
final boolean includeTests,
NotNullPairFunction<ContentEntry, Predicate<ContentFolderTypeProvider>, String[]> funForSources,
NotNullPairFunction<ModuleRootModel, Predicate<ContentFolderTypeProvider>, String[]> funForRuntime) {
ModuleRootsProcessor rootsProcessor = ModuleRootsProcessor.findRootsProcessor(rootModel);
if (type.equals(SourcesOrderRootType.getInstance())) {
if (includeProduction) {
Predicate<ContentFolderTypeProvider> predicate = includeTests ? ContentFolderScopes.productionAndTest() : ContentFolderScopes.production();
if(rootsProcessor != null) {
rootsProcessor.processFileUrls(rootModel, predicate, new CommonProcessors.CollectProcessor<String>(result));
}
else {
for (ContentEntry entry : rootModel.getContentEntries()) {
Collections.addAll(result, funForSources.fun(entry, predicate));
}
}
}
else {
if(rootsProcessor != null) {
rootsProcessor.processFileUrls(rootModel, ContentFolderScopes.test(), new CommonProcessors.CollectProcessor<String>(result));
}
else {
for (ContentEntry entry : rootModel.getContentEntries()) {
Collections.addAll(result, funForSources.fun(entry, ContentFolderScopes.test()));
}
}
}
}
else if (type.equals(BinariesOrderRootType.getInstance())) {
if (myWithoutSelfModuleOutput && myOrderEnumerator.isRootModuleModel(rootModel)) {
if (includeTests && includeProduction) {
Collections.addAll(result, funForRuntime.fun(rootModel, ContentFolderScopes.productionAndTest()));
}
}
else {
Collections.addAll(result, funForRuntime
.fun(rootModel, includeTests ? ContentFolderScopes.productionAndTest() : ContentFolderScopes.production()));
}
}
}
private OrderRootType getRootType(OrderEntry e) {
return myRootType != null ? myRootType : myRootTypeProvider.fun(e);
}
}