| /* |
| * 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; |
| } |
| } |
| } |