All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.teamscale.jacoco.agent.options.FilePatternResolver Maven / Gradle / Ivy

Go to download

JVM profiler that simplifies various aspects around recording and uploading test coverage

There is a newer version: 34.0.2
Show newest version
package com.teamscale.jacoco.agent.options;

import com.teamscale.report.util.ILogger;
import org.conqat.lib.commons.collections.CollectionUtils;
import org.conqat.lib.commons.collections.Pair;
import org.conqat.lib.commons.filesystem.AntPatternUtils;
import org.conqat.lib.commons.filesystem.FileSystemUtils;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;
import java.util.regex.Pattern;

import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;

/** Helper class to support resolving file paths which may contain Ant patterns. */
public class FilePatternResolver {

	/** Stand-in for the question mark operator. */
	private static final String QUESTION_REPLACEMENT = "!@";

	/** Stand-in for the asterisk operator. */
	private static final String ASTERISK_REPLACEMENT = "#@";

	private final ILogger logger;

	public FilePatternResolver(ILogger logger) {
		this.logger = logger;
	}

	/**
	 * Interprets the given pattern as an Ant pattern and resolves it to one existing {@link Path}. If the given path is
	 * relative, it is resolved relative to the current working directory. If more than one file matches the pattern,
	 * one of the matching files is used without any guarantees as to which. The selection is, however, guaranteed to be
	 * deterministic, i.e. if you run the pattern twice and get the same set of files, the same file will be picked each
	 * time.
	 */
	public Path parsePath(String optionName, String pattern) throws AgentOptionParseException {
		return parsePath(optionName, pattern, new File("."));
	}

	/**
	 * Interprets the given pattern as an Ant pattern and resolves it to one or multiple existing {@link File}s. If the
	 * given path is relative, it is resolved relative to the current working directory.
	 */
	public List resolveToMultipleFiles(String optionName, String pattern) throws AgentOptionParseException {
		return resolveToMultipleFiles(optionName, pattern, new File("."));
	}

	/**
	 * Interprets the given pattern as an Ant pattern and resolves it to one or multiple existing {@link File}s. If the
	 * given path is relative, it is resolved relative to the current working directory.
	 * 

* Visible for testing only. */ /* package */ List resolveToMultipleFiles(String optionName, String pattern, File workingDirectory) throws AgentOptionParseException { if (isPathWithPattern(pattern)) { return CollectionUtils .map(parseFileFromPattern(optionName, pattern, workingDirectory).getAllMatchingPaths(), Path::toFile); } try { return Collections.singletonList(workingDirectory.toPath().resolve(Paths.get(pattern)).toFile()); } catch (InvalidPathException e) { throw new AgentOptionParseException("Invalid path given for option " + optionName + ": " + pattern, e); } } /** * Interprets the given pattern as an Ant pattern and resolves it to one existing {@link Path}. If the given path is * relative, it is resolved relative to the given working directory. If more than one file matches the pattern, one * of the matching files is used without any guarantees as to which. The selection is, however, guaranteed to be * deterministic, i.e. if you run the pattern twice and get the same set of files, the same file will be picked each * time. */ /* package */ Path parsePath(String optionName, String pattern, File workingDirectory) throws AgentOptionParseException { if (isPathWithPattern(pattern)) { return parseFileFromPattern(optionName, pattern, workingDirectory).getSinglePath(); } try { return workingDirectory.toPath().resolve(Paths.get(pattern)); } catch (InvalidPathException e) { throw new AgentOptionParseException("Invalid path given for option " + optionName + ": " + pattern, e); } } /** Parses the pattern as a Ant pattern to one or multiple files or directories. */ private FilePatternResolverRun parseFileFromPattern(String optionName, String pattern, File workingDirectory) throws AgentOptionParseException { return new FilePatternResolverRun(logger, optionName, pattern, workingDirectory).resolve(); } /** Returns whether the given path contains Ant pattern characters (?,*). */ private static boolean isPathWithPattern(String path) { return path.contains("?") || path.contains("*"); } /** * Returns whether the given path contains artificial pattern characters ({@link #QUESTION_REPLACEMENT}, {@link * #ASTERISK_REPLACEMENT}). */ private static boolean isPathWithArtificialPattern(String path) { return path.contains(QUESTION_REPLACEMENT) || path.contains(ASTERISK_REPLACEMENT); } private static class FilePatternResolverRun { private final File workingDirectory; private final String optionName; private final String pattern; private String suffixPattern; private Path basePath; private List matchingPaths; private final ILogger logger; private FilePatternResolverRun(ILogger logger, String optionName, String pattern, File workingDirectory) { this.logger = logger; this.optionName = optionName; this.pattern = pattern; this.workingDirectory = workingDirectory.getAbsoluteFile(); splitIntoBasePathAndPattern(pattern); } /** * Resolves the pattern. The results can be retrieved via {@link #getSinglePath()} or {@link * #getAllMatchingPaths()}. */ private FilePatternResolverRun resolve() throws AgentOptionParseException { Pattern pathRegex = AntPatternUtils.convertPattern(suffixPattern, false); Predicate filter = path -> pathRegex .matcher(FileSystemUtils.normalizeSeparators(basePath.relativize(path).toString())).matches(); try { matchingPaths = Files.walk(basePath).filter(filter).sorted().collect(toList()); } catch (IOException e) { throw new AgentOptionParseException( "Could not recursively list files in directory " + basePath + " in order to resolve pattern " + suffixPattern + " given for option " + optionName, e); } return this; } /** * Splits the path into a base dir, i.e. the directory-prefix of the path that does not contain any ? or * * placeholders, and a pattern suffix. We need to replace the pattern characters with stand-ins, because ? and * * are not allowed as path characters on windows. */ private void splitIntoBasePathAndPattern(String value) { String pathWithArtificialPattern = value.replace("?", QUESTION_REPLACEMENT) .replace("*", ASTERISK_REPLACEMENT); Path pathWithPattern = Paths.get(pathWithArtificialPattern); Path baseDir = pathWithPattern; while (isPathWithArtificialPattern(baseDir.toString())) { baseDir = baseDir.getParent(); if (baseDir == null) { suffixPattern = value; basePath = workingDirectory.toPath().resolve("").normalize().toAbsolutePath(); return; } } String pattern = baseDir.relativize(pathWithPattern).toString().replace(QUESTION_REPLACEMENT, "?") .replace(ASTERISK_REPLACEMENT, "*"); Pair baseDirAndPattern = new Pair<>(baseDir.toString(), pattern); suffixPattern = baseDirAndPattern.getSecond(); basePath = workingDirectory.toPath().resolve(baseDir).normalize().toAbsolutePath(); } /** Returns the result of a resolution as a single Path and warns when multiple paths match. */ private Path getSinglePath() throws AgentOptionParseException { if (this.matchingPaths.isEmpty()) { throw new AgentOptionParseException( "Invalid path given for option " + optionName + ": " + this.pattern + ". The pattern " + this.suffixPattern + " did not match any files in " + this.basePath.toAbsolutePath()); } else if (this.matchingPaths.size() > 1) { logger.warn( "Multiple files match the pattern " + this.suffixPattern + " in " + this.basePath .toString() + " for option " + optionName + "! " + "The first one is used, but consider to adjust the " + "pattern to match only one file. Candidates are: " + this.matchingPaths.stream() .map(this.basePath::relativize).map(Path::toString).collect(joining(", "))); } Path path = this.matchingPaths.get(0).normalize(); logger.info("Using file " + path + " for option " + optionName); return path; } /** Returns all matched paths after the resolution. */ private List getAllMatchingPaths() { if (this.matchingPaths.isEmpty()) { logger.warn( "The pattern " + this.suffixPattern + " in " + this.basePath .toString() + " for option " + optionName + " did not match any file!"); } logger.info("Resolved " + pattern + " to " + this.matchingPaths.size() + " for option " + optionName); return this.matchingPaths; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy