| /* |
| * 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 org.jetbrains.jps.builders.impl; |
| |
| import com.intellij.openapi.util.Key; |
| import com.intellij.openapi.util.io.FileUtil; |
| import com.intellij.openapi.util.io.FileUtilRt; |
| import com.intellij.util.SmartList; |
| import gnu.trove.THashMap; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.jetbrains.jps.builders.*; |
| import org.jetbrains.jps.builders.java.JavaModuleBuildTargetType; |
| import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor; |
| import org.jetbrains.jps.builders.storage.BuildDataPaths; |
| import org.jetbrains.jps.incremental.CompileContext; |
| import org.jetbrains.jps.incremental.TargetTypeRegistry; |
| import org.jetbrains.jps.indices.IgnoredFileIndex; |
| import org.jetbrains.jps.indices.ModuleExcludeIndex; |
| import org.jetbrains.jps.model.JpsModel; |
| import org.jetbrains.jps.service.JpsServiceManager; |
| |
| import java.io.File; |
| import java.io.FileFilter; |
| import java.util.*; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.concurrent.ConcurrentMap; |
| |
| /** |
| * @author nik |
| */ |
| public class BuildRootIndexImpl implements BuildRootIndex { |
| private static final Key<Map<File, BuildRootDescriptor>> ROOT_DESCRIPTOR_MAP = Key.create("_root_to_descriptor_map"); |
| private static final Key<Map<BuildTarget<?>, List<? extends BuildRootDescriptor>>> TEMP_TARGET_ROOTS_MAP = Key.create("_module_to_root_map"); |
| private final IgnoredFileIndex myIgnoredFileIndex; |
| private HashMap<BuildTarget<?>, List<? extends BuildRootDescriptor>> myRootsByTarget; |
| private THashMap<File,List<BuildRootDescriptor>> myRootToDescriptors; |
| private ConcurrentMap<BuildRootDescriptor, FileFilter> myFileFilters; |
| |
| public BuildRootIndexImpl(BuildTargetIndex targetIndex, JpsModel model, ModuleExcludeIndex index, |
| BuildDataPaths dataPaths, final IgnoredFileIndex ignoredFileIndex) { |
| myIgnoredFileIndex = ignoredFileIndex; |
| myRootsByTarget = new HashMap<BuildTarget<?>, List<? extends BuildRootDescriptor>>(); |
| myRootToDescriptors = new THashMap<File, List<BuildRootDescriptor>>(FileUtil.FILE_HASHING_STRATEGY); |
| myFileFilters = new ConcurrentHashMap<BuildRootDescriptor, FileFilter>(16, 0.75f, 1); |
| final Iterable<AdditionalRootsProviderService> rootsProviders = JpsServiceManager.getInstance().getExtensions(AdditionalRootsProviderService.class); |
| for (BuildTargetType<?> targetType : TargetTypeRegistry.getInstance().getTargetTypes()) { |
| for (BuildTarget<?> target : targetIndex.getAllTargets(targetType)) { |
| addRoots(dataPaths, rootsProviders, target, model, index, ignoredFileIndex); |
| } |
| } |
| } |
| |
| private <R extends BuildRootDescriptor> void addRoots(BuildDataPaths dataPaths, Iterable<AdditionalRootsProviderService> rootsProviders, |
| BuildTarget<R> target, |
| JpsModel model, |
| ModuleExcludeIndex index, |
| IgnoredFileIndex ignoredFileIndex) { |
| List<R> descriptors = target.computeRootDescriptors(model, index, ignoredFileIndex, dataPaths); |
| for (AdditionalRootsProviderService<?> provider : rootsProviders) { |
| if (provider.getTargetTypes().contains(target.getTargetType())) { |
| //noinspection unchecked |
| AdditionalRootsProviderService<R> providerService = (AdditionalRootsProviderService<R>)provider; |
| final List<R> additionalRoots = providerService.getAdditionalRoots(target, dataPaths); |
| if (!additionalRoots.isEmpty()) { |
| descriptors = new ArrayList<R>(descriptors); |
| descriptors.addAll(additionalRoots); |
| } |
| } |
| } |
| for (BuildRootDescriptor descriptor : descriptors) { |
| registerDescriptor(descriptor); |
| } |
| if (descriptors instanceof ArrayList<?>) { |
| ((ArrayList)descriptors).trimToSize(); |
| } |
| myRootsByTarget.put(target, descriptors); |
| } |
| |
| private void registerDescriptor(BuildRootDescriptor descriptor) { |
| List<BuildRootDescriptor> list = myRootToDescriptors.get(descriptor.getRootFile()); |
| if (list == null) { |
| list = new SmartList<BuildRootDescriptor>(); |
| myRootToDescriptors.put(descriptor.getRootFile(), list); |
| } |
| list.add(descriptor); |
| } |
| |
| @NotNull |
| @Override |
| public <R extends BuildRootDescriptor> List<R> getRootDescriptors(@NotNull File root, |
| @Nullable Collection<? extends BuildTargetType<? extends BuildTarget<R>>> types, |
| @Nullable CompileContext context) { |
| List<BuildRootDescriptor> descriptors = myRootToDescriptors.get(root); |
| List<R> result = new SmartList<R>(); |
| if (descriptors != null) { |
| for (BuildRootDescriptor descriptor : descriptors) { |
| if (types == null || types.contains(descriptor.getTarget().getTargetType())) { |
| //noinspection unchecked |
| result.add((R)descriptor); |
| } |
| } |
| } |
| if (context != null) { |
| final Map<File, BuildRootDescriptor> contextMap = ROOT_DESCRIPTOR_MAP.get(context); |
| if (contextMap != null) { |
| BuildRootDescriptor descriptor = contextMap.get(root); |
| if (descriptor != null && (types == null || types.contains(descriptor.getTarget().getTargetType()))) { |
| //noinspection unchecked |
| result.add((R)descriptor); |
| } |
| } |
| } |
| return result; |
| } |
| |
| @NotNull |
| @Override |
| public <R extends BuildRootDescriptor> List<R> getTargetRoots(@NotNull BuildTarget<R> target, CompileContext context) { |
| //noinspection unchecked |
| List<R> roots = (List<R>)myRootsByTarget.get(target); |
| if (context != null) { |
| final List<R> tempDescriptors = getTempTargetRoots(target, context); |
| if (!tempDescriptors.isEmpty()) { |
| if (roots != null) { |
| roots = new ArrayList<R>(roots); |
| roots.addAll(tempDescriptors); |
| } |
| else { |
| roots = tempDescriptors; |
| } |
| } |
| } |
| return roots != null? Collections.unmodifiableList(roots) : Collections.<R>emptyList(); |
| } |
| |
| @NotNull |
| @Override |
| public <R extends BuildRootDescriptor> List<R> getTempTargetRoots(@NotNull BuildTarget<R> target, @NotNull CompileContext context) { |
| final Map<BuildTarget<?>, List<? extends BuildRootDescriptor>> contextMap = TEMP_TARGET_ROOTS_MAP.get(context); |
| //noinspection unchecked |
| final List<R> rootList = contextMap != null? (List<R>)contextMap.get(target) : null; |
| return rootList != null ? rootList : Collections.<R>emptyList(); |
| } |
| |
| @Override |
| public <R extends BuildRootDescriptor> void associateTempRoot(@NotNull CompileContext context, @NotNull BuildTarget<R> target, @NotNull R root) { |
| Map<File, BuildRootDescriptor> rootToDescriptorMap = ROOT_DESCRIPTOR_MAP.get(context); |
| if (rootToDescriptorMap == null) { |
| rootToDescriptorMap = new THashMap<File, BuildRootDescriptor>(FileUtil.FILE_HASHING_STRATEGY); |
| ROOT_DESCRIPTOR_MAP.set(context, rootToDescriptorMap); |
| } |
| |
| Map<BuildTarget<?>, List<? extends BuildRootDescriptor>> targetToRootMap = TEMP_TARGET_ROOTS_MAP.get(context); |
| if (targetToRootMap == null) { |
| targetToRootMap = new HashMap<BuildTarget<?>, List<? extends BuildRootDescriptor>>(); |
| TEMP_TARGET_ROOTS_MAP.set(context, targetToRootMap); |
| } |
| |
| final BuildRootDescriptor d = rootToDescriptorMap.get(root.getRootFile()); |
| if (d != null) { |
| return; |
| } |
| |
| //noinspection unchecked |
| List<R> targetRoots = (List<R>)targetToRootMap.get(target); |
| if (targetRoots == null) { |
| targetRoots = new ArrayList<R>(); |
| targetToRootMap.put(target, targetRoots); |
| } |
| rootToDescriptorMap.put(root.getRootFile(), root); |
| targetRoots.add(root); |
| } |
| |
| @Override |
| @Nullable |
| public <R extends BuildRootDescriptor> R findParentDescriptor(@NotNull File file, @NotNull Collection<? extends BuildTargetType<? extends BuildTarget<R>>> types, |
| @Nullable CompileContext context) { |
| File current = file; |
| int depth = 0; |
| while (current != null) { |
| final List<R> descriptors = filterDescriptorsByFile(getRootDescriptors(current, types, context), file, depth); |
| if (!descriptors.isEmpty()) { |
| return descriptors.get(0); |
| } |
| current = FileUtilRt.getParentFile(current); |
| depth++; |
| } |
| return null; |
| } |
| |
| @Override |
| @NotNull |
| public <R extends BuildRootDescriptor> Collection<R> findAllParentDescriptors(@NotNull File file, |
| @Nullable Collection<? extends BuildTargetType<? extends BuildTarget<R>>> types, |
| @Nullable CompileContext context) { |
| File current = file; |
| List<R> result = null; |
| int depth = 0; |
| while (current != null) { |
| List<R> descriptors = filterDescriptorsByFile(getRootDescriptors(current, types, context), file, depth); |
| if (!descriptors.isEmpty()) { |
| if (result == null) { |
| result = descriptors; |
| } |
| else { |
| result = new ArrayList<R>(result); |
| result.addAll(descriptors); |
| } |
| } |
| current = FileUtilRt.getParentFile(current); |
| depth++; |
| } |
| return result != null ? result : Collections.<R>emptyList(); |
| } |
| |
| @NotNull |
| private <R extends BuildRootDescriptor> List<R> filterDescriptorsByFile(@NotNull List<R> descriptors, File file, int parentsToCheck) { |
| List<R> result = descriptors; |
| for (int i = 0; i < descriptors.size(); i++) { |
| R descriptor = descriptors.get(i); |
| if (isFileAccepted(file, descriptor) && isParentDirectoriesAccepted(file, parentsToCheck, descriptor)) { |
| if (result != descriptors) { |
| result.add(descriptor); |
| } |
| } |
| else if (result == descriptors) { |
| result = new ArrayList<R>(descriptors.size() - 1); |
| for (int j = 0; j < i; j++) { |
| result.add(descriptors.get(j)); |
| } |
| } |
| } |
| return result; |
| } |
| |
| private boolean isParentDirectoriesAccepted(File file, int parentsToCheck, BuildRootDescriptor descriptor) { |
| File current = file; |
| while (parentsToCheck-- > 0) { |
| current = FileUtil.getParentFile(current); |
| if (!isDirectoryAccepted(current, descriptor)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| @NotNull |
| @Override |
| public <R extends BuildRootDescriptor> Collection<R> findAllParentDescriptors(@NotNull File file, @Nullable CompileContext context) { |
| return findAllParentDescriptors(file, null, context); |
| } |
| |
| @Override |
| @NotNull |
| public Collection<? extends BuildRootDescriptor> clearTempRoots(@NotNull CompileContext context) { |
| try { |
| final Map<File, BuildRootDescriptor> map = ROOT_DESCRIPTOR_MAP.get(context); |
| return map != null? map.values() : Collections.<BuildRootDescriptor>emptyList(); |
| } |
| finally { |
| TEMP_TARGET_ROOTS_MAP.set(context, null); |
| ROOT_DESCRIPTOR_MAP.set(context, null); |
| } |
| } |
| |
| @Override |
| @Nullable |
| public JavaSourceRootDescriptor findJavaRootDescriptor(@Nullable CompileContext context, File file) { |
| return findParentDescriptor(file, JavaModuleBuildTargetType.ALL_TYPES, context); |
| } |
| |
| @NotNull |
| @Override |
| public FileFilter getRootFilter(@NotNull BuildRootDescriptor descriptor) { |
| FileFilter filter = myFileFilters.get(descriptor); |
| if (filter == null) { |
| filter = descriptor.createFileFilter(); |
| myFileFilters.put(descriptor, filter); |
| } |
| return filter; |
| } |
| |
| @Override |
| public boolean isFileAccepted(@NotNull File file, @NotNull BuildRootDescriptor descriptor) { |
| return !myIgnoredFileIndex.isIgnored(file.getName()) && getRootFilter(descriptor).accept(file); |
| } |
| |
| @Override |
| public boolean isDirectoryAccepted(@NotNull File dir, @NotNull BuildRootDescriptor descriptor) { |
| return !myIgnoredFileIndex.isIgnored(dir.getName()) && !descriptor.getExcludedRoots().contains(dir); |
| } |
| } |