/*
* Copyright 2000-2015 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.execution.Location;
import com.intellij.execution.PsiLocation;
import com.intellij.execution.testframework.sm.runner.SMTestLocator;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.io.URLUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @author Roman Chernyatchik
*/
public class FileUrlProvider implements SMTestLocator, DumbAware {
private static final Logger LOG = Logger.getInstance(FileUrlProvider.class.getName());
public static final FileUrlProvider INSTANCE = new FileUrlProvider();
@NotNull
@Override
public List<Location> getLocation(@NotNull String protocol, @NotNull String path, @NotNull Project project, @NotNull GlobalSearchScope scope) {
if (!URLUtil.FILE_PROTOCOL.equals(protocol)) {
return Collections.emptyList();
}
final String normalizedPath = path.replace(File.separatorChar, '/');
final int lineNoSeparatorIndex = normalizedPath.lastIndexOf(':');
final String filePath;
final int lineNumber;
// if line is specified
if (lineNoSeparatorIndex > 3) { // on Windows, paths start with /C: and that colon is not a line number separator
final String lineNumStr = normalizedPath.substring(lineNoSeparatorIndex + 1);
int lineNum = 0;
try {
lineNum = Integer.parseInt(lineNumStr);
} catch (NumberFormatException e) {
LOG.warn(protocol + ": Malformed location path: " + path, e);
}
filePath = normalizedPath.substring(0, lineNoSeparatorIndex);
lineNumber = lineNum;
} else {
// unknown line
lineNumber = 1;
filePath = normalizedPath;
}
// Now we should search file with most suitable path
// here path may be absolute or relative
final String systemIndependentPath = FileUtil.toSystemIndependentName(filePath);
final List<VirtualFile> virtualFiles = TestsLocationProviderUtil.findSuitableFilesFor(systemIndependentPath, project);
if (virtualFiles.isEmpty()) {
return Collections.emptyList();
}
if (lineNumber < 0) {
LOG.warn("Tests location provider: line number should be >= 1. Path: " + path);
}
final List<Location> locations = new ArrayList<Location>(2);
for (VirtualFile file : virtualFiles) {
locations.add(createLocationFor(project, file, lineNumber < 1 ? 1 : lineNumber));
}
return locations;
}
@Nullable
public static Location createLocationFor(Project project, @NotNull VirtualFile virtualFile, int lineNum) {
assert lineNum > 0;
final PsiFile psiFile = PsiManager.getInstance(project).findFile(virtualFile);
if (psiFile == null) {
return null;
}
final Document doc = PsiDocumentManager.getInstance(project).getDocument(psiFile);
if (doc == null) {
return null;
}
final int lineCount = doc.getLineCount();
final int lineStartOffset;
final int endOffset;
if (lineNum <= lineCount) {
lineStartOffset = doc.getLineStartOffset(lineNum - 1);
endOffset = doc.getLineEndOffset(lineNum - 1);
} else {
// unknown line
lineStartOffset = 0;
endOffset = doc.getTextLength();
}
int offset = lineStartOffset;
PsiElement elementAtLine = null;
while (offset <= endOffset) {
elementAtLine = psiFile.findElementAt(offset);
if (!(elementAtLine instanceof PsiWhiteSpace)) break;
int length = elementAtLine.getTextLength();
offset += length > 1 ? length - 1 : 1;
}
return PsiLocation.fromPsiElement(project, elementAtLine != null ? elementAtLine : psiFile);
}
}