blob: bdfde7ea61e9cba66d52924b7165970edaeee21a [file] [log] [blame]
/*
* Copyright 2000-2009 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.execution.testframework.sm;
import com.intellij.navigation.ChooseByNameContributor;
import com.intellij.navigation.NavigationItem;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.ex.temp.TempFileSystem;
import com.intellij.psi.PsiFile;
import com.intellij.util.text.StringTokenizer;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
* @author Roman Chernyatchik
*/
public class TestsLocationProviderUtil {
@NonNls private static final String PROTOCOL_SEPARATOR = "://";
private static final int MIN_PROXIMITY_THRESHOLD = 1;
private TestsLocationProviderUtil() {
}
@Nullable
public static String extractPath(@NotNull final String locationUrl) {
final int index = locationUrl.indexOf(PROTOCOL_SEPARATOR);
if (index >= 0) {
return locationUrl.substring(index + PROTOCOL_SEPARATOR.length());
}
return null;
}
public static List<VirtualFile> findSuitableFilesFor(final String filePath, final Project project) {
final ProjectFileIndex index = ProjectRootManager.getInstance(project).getFileIndex();
// at first let's try to find file as is, by it's real path
// and check that file belongs to current project
// this location provider designed for tests thus we will check only project content
// (we cannot check just sources or tests folders because RM doesn't use it
final VirtualFile file = getByFullPath(filePath);
final boolean inProjectContent = file != null && (index.isInContent(file));
if (inProjectContent) {
return Collections.singletonList(file);
}
//split file by "/" in parts
final LinkedList<String> folders = new LinkedList<String>();
final StringTokenizer st = new StringTokenizer(filePath, "/", false);
String fileName = null;
while (st.hasMoreTokens()) {
final String pathComponent = st.nextToken();
if (st.hasMoreTokens()) {
folders.addFirst(pathComponent);
} else {
// last token
fileName = pathComponent;
}
}
if (fileName == null) {
return Collections.emptyList();
}
return findFilesClosestToTarget(folders, collectCandidates(project, fileName, true), MIN_PROXIMITY_THRESHOLD);
}
/**
* Looks for files with given name which are close to given path
* @param targetParentFolders folders path
* @param candidates
* @param minProximityThreshold
* @return
*/
public static List<VirtualFile> findFilesClosestToTarget(@NotNull final List<String> targetParentFolders,
final List<FileInfo> candidates,
final int minProximityThreshold) {
// let's find all files with similar relative path
if (candidates.isEmpty()) {
return Collections.emptyList();
}
// let's iterate relative path components and determine which files are closer to our relative path
for (String folderName : targetParentFolders) {
for (FileInfo info : candidates) {
info.processRelativePathComponent(folderName);
}
}
// let's extract the closest files to relative path. For this we will find max proximity and and
// we also assume that relative files and folders should have at least one common parent folder - just
// to remove false positives on some cases
int maxProximity = 0;
for (FileInfo fileInfo : candidates) {
final int proximity = fileInfo.getProximity();
if (proximity > maxProximity) {
maxProximity = proximity;
}
}
if (maxProximity >= minProximityThreshold) {
final List<VirtualFile> files = new ArrayList<VirtualFile>();
for (FileInfo info : candidates) {
if (info.getProximity() == maxProximity) {
files.add(info.getFile());
}
}
return files;
}
return Collections.emptyList();
}
public static List<FileInfo> collectCandidates(final Project project, final String fileName,
final boolean includeNonProjectItems) {
final List<FileInfo> filesInfo = new ArrayList<FileInfo>();
final ChooseByNameContributor[] contributors = Extensions.getExtensions(ChooseByNameContributor.FILE_EP_NAME);
for (ChooseByNameContributor contributor : contributors) {
// let's find files with same name in project and libraries
final NavigationItem[] navigationItems = contributor.getItemsByName(fileName, fileName, project, includeNonProjectItems);
for (NavigationItem navigationItem : navigationItems) {
if (navigationItem instanceof PsiFile) {
final VirtualFile itemFile = ((PsiFile)navigationItem).getVirtualFile();
assert itemFile != null;
filesInfo.add(new FileInfo(itemFile));
}
}
}
return filesInfo;
}
@Nullable
private static VirtualFile getByFullPath(String filePath) {
final VirtualFile fileByPath = LocalFileSystem.getInstance().findFileByPath(filePath);
if (fileByPath != null) {
return fileByPath;
}
// if we are in UnitTest mode probably TempFileSystem is used instead of LocalFileSystem
if (ApplicationManager.getApplication().isUnitTestMode()) {
return TempFileSystem.getInstance().findFileByPath(filePath);
}
return null;
}
public static class FileInfo {
private final VirtualFile myFile;
private VirtualFile myCurrentFolder;
private int myProximity = 0;
public FileInfo(VirtualFile file) {
myFile = file;
myCurrentFolder = myFile.getParent();
}
public void processRelativePathComponent(final String folderName) {
if (myCurrentFolder == null) {
return;
}
if (!folderName.equals(myCurrentFolder.getName())) {
// if one of path components differs - no sense in checking others
myCurrentFolder = null;
return;
}
// common folder was found, let's increase proximity degree and move to parent folder
myProximity ++;
myCurrentFolder = myCurrentFolder.getParent();
}
public VirtualFile getFile() {
return myFile;
}
public int getProximity() {
return myProximity;
}
}
}