blob: 1c5d1167f0922747ba51c4ac12e68bf9bae4b05a [file] [log] [blame]
/*
* Copyright 2014 The Kythe Authors. All rights reserved.
*
* 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.google.devtools.kythe.extractors.shared;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.StandardSystemProperty.USER_DIR;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.hash.Hashing.sha256;
import static java.util.stream.Collectors.toCollection;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Ordering;
import com.google.common.collect.Streams;
import com.google.common.io.Files;
import com.google.common.util.concurrent.SettableFuture;
import com.google.devtools.kythe.proto.Analysis.CompilationUnit;
import com.google.devtools.kythe.proto.Analysis.CompilationUnit.FileInput;
import com.google.devtools.kythe.proto.Analysis.FileData;
import com.google.devtools.kythe.proto.Analysis.FileInfo;
import com.google.devtools.kythe.proto.Storage.VName;
import com.google.protobuf.ByteString;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;
/**
* A class containing common utilities used by language extractors.
*
* <p>This class is a bit of a mishmash. It includes helpers for:
*
* <ul>
* <li>dealing with Google 3 specific file paths
* <li>loading files into FileData
* <li>converting from byte[] to FileData
* </ul>
*/
// TODO: Split this class by domain.
public class ExtractorUtils {
public static final Comparator<FileInput> FILE_INPUT_COMPARATOR =
Comparator.comparing(FileInput::getInfo, Comparator.comparing(FileInfo::getPath));
/**
* Creates fully populated FileInput protocol buffers based on a provided set of files.
*
* @param filePathToFileDatas map with file contents.
* @return fully populated FileInput protos
* @throws ExtractionException
*/
public static List<FileData> convertBytesToFileDatas(
final Map<String, byte[]> filePathToFileContents) throws ExtractionException {
checkNotNull(filePathToFileContents);
return filePathToFileContents.keySet().stream()
.map(path -> createFileData(path, filePathToFileContents.get(path)))
.collect(toCollection(ArrayList::new));
}
public static FileData createFileData(String path, byte[] content) {
return createFileData(path, getContentDigest(content), content);
}
public static List<FileData> processRequiredInputs(Iterable<String> files)
throws ExtractionException {
final SettableFuture<ExtractionException> exception = SettableFuture.create();
List<FileData> result =
Streams.stream(files)
.map(
path -> {
byte[] content = new byte[0];
try {
content = Files.toByteArray(new File(path));
} catch (IOException e) {
exception.set(new ExtractionException(e, false));
}
if (content == null) {
exception.set(
new ExtractionException(
String.format("Unable to locate required input %s", path), false));
return null;
}
String digest = getContentDigest(content);
return createFileData(path, digest, content);
})
.collect(toCollection(ArrayList::new));
if (exception.isDone()) {
try {
throw exception.get();
} catch (InterruptedException e) {
throw new ExtractionException(e, true);
} catch (ExecutionException e) {
throw new ExtractionException(e, false);
}
}
return result;
}
public static List<FileInput> toFileInputs(Iterable<FileData> fileDatas) {
return toFileInputs(FileVNames.staticCorpus(""), p -> p, fileDatas);
}
public static Function<String, String> makeRelativizer(final Path rootDir) {
return p -> tryMakeRelative(rootDir, Paths.get(p));
}
public static List<FileInput> toFileInputs(
FileVNames fileVNames, Function<String, String> relativize, Iterable<FileData> fileDatas) {
return Streams.stream(fileDatas)
.map(
fileData -> {
VName vname = lookupVName(fileVNames, relativize, fileData.getInfo().getPath());
return FileInput.newBuilder().setInfo(fileData.getInfo()).setVName(vname).build();
})
.sorted(FILE_INPUT_COMPARATOR)
.collect(toImmutableList());
}
public static VName lookupVName(
FileVNames fileVNames, Function<String, String> relativize, String path) {
String relativePath = relativize.apply(path);
VName vname = fileVNames.lookupBaseVName(relativePath);
if (vname.getPath().isEmpty()) {
vname = vname.toBuilder().setPath(relativePath).build();
}
return vname;
}
/** Tries to make a path relative to a root directory. Returns the fullpath otherwise. */
public static String tryMakeRelative(String rootDir, String path) {
return tryMakeRelative(Paths.get(rootDir), Paths.get(path));
}
/** Tries to make a path relative to a root directory. Returns the fullpath otherwise. */
public static String tryMakeRelative(Path rootDir, Path path) {
Path absRoot = rootDir.toAbsolutePath().normalize();
Path absPath = path.toAbsolutePath().normalize();
Path relPath = absRoot.relativize(absPath).normalize();
if (relPath.toString().isEmpty()) {
return ".";
}
return (relPath.startsWith("..") ? absPath : relPath).toString();
}
public static String getCurrentWorkingDirectory() {
return USER_DIR.value();
}
public static String digestForPath(String path) throws NoSuchAlgorithmException, IOException {
return digestForFile(new File(path));
}
public static String digestForFile(File file) throws NoSuchAlgorithmException, IOException {
return getContentDigest(Files.toByteArray(file));
}
/** Computes a digest over the contents of a file. */
@VisibleForTesting
public static String getContentDigest(byte[] content) {
return sha256().hashBytes(content).toString();
}
public static CompilationUnit normalizeCompilationUnit(CompilationUnit existingCompilationUnit) {
CompilationUnit.Builder builder = CompilationUnit.newBuilder(existingCompilationUnit);
List<FileInput> oldRequiredInputs =
Ordering.from(FILE_INPUT_COMPARATOR).sortedCopy(builder.getRequiredInputList());
builder.clearRequiredInput();
builder.addAllRequiredInput(oldRequiredInputs);
existingCompilationUnit = builder.build();
return existingCompilationUnit;
}
private static FileData createFileData(String path, String digest, byte[] content) {
return FileData.newBuilder()
.setContent(ByteString.copyFrom(content))
.setInfo(FileInfo.newBuilder().setDigest(digest).setPath(path))
.build();
}
}