/* * Copyright 2000-2016 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.fileEditor.impl; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.UniqueVFilePathBuilder; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.io.UniqueNameBuilder; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFilePathWrapper; import com.intellij.psi.search.FilenameIndex; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.CachedValue; import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.util.CachedValuesManager; import com.intellij.psi.util.PsiModificationTracker; import com.intellij.util.ConcurrencyUtil; import com.intellij.util.containers.ContainerUtil; import gnu.trove.THashSet; 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.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * @author yole */ public class UniqueVFilePathBuilderImpl extends UniqueVFilePathBuilder { @NotNull @Override public String getUniqueVirtualFilePath(Project project, VirtualFile file, GlobalSearchScope scope) { return getUniqueVirtualFilePath(project, file, false, scope); } @Override public String getUniqueVirtualFilePath(Project project, VirtualFile vFile) { return getUniqueVirtualFilePath(project, vFile, GlobalSearchScope.projectScope(project)); } @NotNull @Override public String getUniqueVirtualFilePathWithinOpenedFileEditors(Project project, VirtualFile vFile) { return getUniqueVirtualFilePath(project, vFile, true, GlobalSearchScope.projectScope(project)); } private static final Key<CachedValue<Map<GlobalSearchScope,Map<String, UniqueNameBuilder<VirtualFile>>>>> ourShortNameBuilderCacheKey = Key.create("project's.short.file.name.builder"); private static final Key<CachedValue<Map<GlobalSearchScope, Map<String, UniqueNameBuilder<VirtualFile>>>>> ourShortNameOpenedBuilderCacheKey = Key.create("project's.short.file.name.opened.builder"); private static final UniqueNameBuilder<VirtualFile> ourEmptyBuilder = new UniqueNameBuilder<>(null, null, -1); private static String getUniqueVirtualFilePath(final Project project, VirtualFile file, final boolean skipNonOpenedFiles, GlobalSearchScope scope) { Key<CachedValue<Map<GlobalSearchScope, Map<String, UniqueNameBuilder<VirtualFile>>>>> key = skipNonOpenedFiles ? ourShortNameOpenedBuilderCacheKey:ourShortNameBuilderCacheKey; CachedValue<Map<GlobalSearchScope, Map<String, UniqueNameBuilder<VirtualFile>>>> data = project.getUserData(key); if (data == null) { project.putUserData(key, data = CachedValuesManager.getManager(project).createCachedValue( () -> new CachedValueProvider.Result<Map<GlobalSearchScope, Map<String, UniqueNameBuilder<VirtualFile>>>>( new ConcurrentHashMap<>(2), PsiModificationTracker.MODIFICATION_COUNT, //ProjectRootModificationTracker.getInstance(project), //VirtualFileManager.VFS_STRUCTURE_MODIFICATIONS, FileEditorManagerImpl.OPEN_FILE_SET_MODIFICATION_COUNT ), false)); } ConcurrentMap<GlobalSearchScope, Map<String, UniqueNameBuilder<VirtualFile>>> scope2ValueMap = (ConcurrentMap<GlobalSearchScope, Map<String,UniqueNameBuilder<VirtualFile>>>)data.getValue(); Map<String, UniqueNameBuilder<VirtualFile>> valueMap = scope2ValueMap.get(scope); if (valueMap == null) { valueMap = ConcurrencyUtil.cacheOrGet(scope2ValueMap, scope, ContainerUtil.createConcurrentSoftValueMap()); } final String fileName = file.getName(); UniqueNameBuilder<VirtualFile> uniqueNameBuilderForShortName = valueMap.get(fileName); if (uniqueNameBuilderForShortName == null) { final UniqueNameBuilder<VirtualFile> builder = filesWithTheSameName( fileName, project, skipNonOpenedFiles, scope ); valueMap.put(fileName, builder != null ? builder:ourEmptyBuilder); uniqueNameBuilderForShortName = builder; } else if (uniqueNameBuilderForShortName == ourEmptyBuilder) { uniqueNameBuilderForShortName = null; } if (uniqueNameBuilderForShortName != null && uniqueNameBuilderForShortName.contains(file)) { if (file instanceof VirtualFilePathWrapper) { return ((VirtualFilePathWrapper)file).getPresentablePath(); } return uniqueNameBuilderForShortName.getShortPath(file); } return file.getName(); } @Nullable private static UniqueNameBuilder<VirtualFile> filesWithTheSameName(String fileName, Project project, boolean skipNonOpenedFiles, GlobalSearchScope scope) { Collection<VirtualFile> filesWithSameName = skipNonOpenedFiles ? Collections.emptySet() : FilenameIndex.getVirtualFilesByName(project, fileName, scope); THashSet<VirtualFile> setOfFilesWithTheSameName = new THashSet<>(filesWithSameName); // add open files out of project scope for(VirtualFile openFile: FileEditorManager.getInstance(project).getOpenFiles()) { if (openFile.getName().equals(fileName)) { setOfFilesWithTheSameName.add(openFile); } } if (!skipNonOpenedFiles) { for (VirtualFile recentlyEditedFile : EditorHistoryManager.getInstance(project).getFiles()) { if (recentlyEditedFile.getName().equals(fileName)) { setOfFilesWithTheSameName.add(recentlyEditedFile); } } } filesWithSameName = setOfFilesWithTheSameName; if (filesWithSameName.size() > 1) { String path = project.getBasePath(); path = path == null ? "" : FileUtil.toSystemIndependentName(path); UniqueNameBuilder<VirtualFile> builder = new UniqueNameBuilder<>(path, File.separator, 25); for (VirtualFile virtualFile: filesWithSameName) { builder.addPath(virtualFile, virtualFile.getPath()); } return builder; } return null; } }