blob: fb9a75176255732379ef75f17b882f02445b78d5 [file] [log] [blame]
/*
* 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.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.roots.*;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ArrayUtil;
import com.intellij.util.NotNullFunction;
import com.intellij.util.PathsList;
import com.intellij.util.Processor;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
/**
* @author nik
*/
public class OrderRootsEnumeratorImpl implements OrderRootsEnumerator {
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) {
collectModuleRoots(type, ((ModuleSourceOrderEntry)orderEntry).getRootModel(), result, true, !myOrderEnumerator.isProductionOnly());
}
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;
collectModuleRoots(type, rootModel, result, !productionOnTests, includeTests);
}
}
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) {
collectModuleRootsUrls(type, ((ModuleSourceOrderEntry)orderEntry).getRootModel(), result, true, !myOrderEnumerator.isProductionOnly());
}
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;
collectModuleRootsUrls(type, rootModel, result, !productionOnTests, includeTests);
}
}
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 collectModuleRoots(OrderRootType type,
ModuleRootModel rootModel,
Collection<VirtualFile> result, final boolean includeProduction, final boolean includeTests) {
if (type.equals(OrderRootType.SOURCES)) {
if (includeProduction) {
Collections.addAll(result, rootModel.getSourceRoots(includeTests));
}
else {
result.addAll(rootModel.getSourceRoots(JavaModuleSourceRootTypes.TESTS));
}
}
else if (type.equals(OrderRootType.CLASSES)) {
final CompilerModuleExtension extension = rootModel.getModuleExtension(CompilerModuleExtension.class);
if (extension != null) {
if (myWithoutSelfModuleOutput && myOrderEnumerator.isRootModuleModel(rootModel)) {
if (includeTests && includeProduction) {
Collections.addAll(result, extension.getOutputRoots(false));
}
}
else {
if (includeProduction) {
Collections.addAll(result, extension.getOutputRoots(includeTests));
}
else {
ContainerUtil.addIfNotNull(result, extension.getCompilerOutputPathForTests());
}
}
}
}
myOrderEnumerator.addCustomRootsForModule(type, rootModel, result, includeProduction, includeTests);
}
private void collectModuleRootsUrls(OrderRootType type,
ModuleRootModel rootModel,
Collection<String> result, final boolean includeProduction, final boolean includeTests) {
if (type.equals(OrderRootType.SOURCES)) {
if (includeProduction) {
Collections.addAll(result, rootModel.getSourceRootUrls(includeTests));
}
else {
for (ContentEntry entry : rootModel.getContentEntries()) {
for (SourceFolder folder : entry.getSourceFolders(JavaModuleSourceRootTypes.TESTS)) {
result.add(folder.getUrl());
}
}
}
}
else if (type.equals(OrderRootType.CLASSES)) {
final CompilerModuleExtension extension = rootModel.getModuleExtension(CompilerModuleExtension.class);
if (extension != null) {
if (myWithoutSelfModuleOutput && myOrderEnumerator.isRootModuleModel(rootModel)) {
if (includeTests && includeProduction) {
Collections.addAll(result, extension.getOutputRootUrls(false));
}
}
else {
if (includeProduction) {
Collections.addAll(result, extension.getOutputRootUrls(includeTests));
}
else {
ContainerUtil.addIfNotNull(result, extension.getCompilerOutputUrlForTests());
}
}
}
}
}
private OrderRootType getRootType(OrderEntry e) {
return myRootType != null ? myRootType : myRootTypeProvider.fun(e);
}
}