diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/PmdAnalysis.java b/pmd-core/src/main/java/net/sourceforge/pmd/PmdAnalysis.java index a73c7cf089..a5a06f4659 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PmdAnalysis.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PmdAnalysis.java @@ -6,10 +6,13 @@ package net.sourceforge.pmd; import static net.sourceforge.pmd.util.CollectionUtil.listOf; +import java.io.File; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -17,6 +20,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.event.Level; @@ -41,7 +45,9 @@ import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.LanguageVersionDiscoverer; import net.sourceforge.pmd.lang.document.FileCollector; +import net.sourceforge.pmd.lang.document.FileId; import net.sourceforge.pmd.lang.document.TextFile; +import net.sourceforge.pmd.renderers.FileNameRenderer; import net.sourceforge.pmd.renderers.Renderer; import net.sourceforge.pmd.reporting.GlobalAnalysisListener; import net.sourceforge.pmd.reporting.ListenerInitializer; @@ -110,7 +116,7 @@ public final class PmdAnalysis implements AutoCloseable { ); for (Path path : config.getRelativizeRoots()) { - this.collector.relativizeWith(path); + this.relativizeWith(path); } } @@ -283,6 +289,45 @@ public final class PmdAnalysis implements AutoCloseable { return langProperties.computeIfAbsent(language, Language::newPropertyBundle); } + private final List relativizeRootPaths = new ArrayList<>(); + /** + * Add a prefix that is used to relativize file paths as their display name. + * For instance, when adding a file {@code /tmp/src/main/java/org/foo.java}, + * and relativizing with {@code /tmp/src/}, the registered {@link TextFile} + * will have a path id of {@code /tmp/src/main/java/org/foo.java}, and a + * display name of {@code main/java/org/foo.java}. + * + *

This only matters for files added from a {@link Path} object. + * + * @param path Path with which to relativize + */ + public void relativizeWith(Path path) { + this.relativizeRootPaths.add(Objects.requireNonNull(path)); + this.relativizeRootPaths.sort(Comparator.naturalOrder()); + } + + public FileNameRenderer getFileNameRenderer() { + return new FileNameRenderer() { + private final List relativizeRootPaths = new ArrayList<>(PmdAnalysis.this.relativizeRootPaths); + + @Override + public String getDisplayName(FileId fileId) { + String localDisplayName = getLocalDisplayName(fileId); + if (fileId.getParentFsPath() != null) { + return getDisplayName(fileId.getParentFsPath()) + "!" + localDisplayName; + } + return localDisplayName; + } + + private String getLocalDisplayName(FileId file) { + if (!relativizeRootPaths.isEmpty()) { + return PmdAnalysis.getDisplayName(file, relativizeRootPaths); + } + return file.getOriginalPath(); + } + }; + } + /** * Run PMD with the current state of this instance. This will start * and finish the registered renderers, and close all @@ -333,7 +378,7 @@ public final class PmdAnalysis implements AutoCloseable { // Initialize listeners try (ListenerInitializer initializer = listener.initializer()) { initializer.setNumberOfFilesToAnalyze(textFiles.size()); - initializer.setFileNameRenderer(files().getFileNameRenderer()); + initializer.setFileNameRenderer(getFileNameRenderer()); } } catch (Exception e) { reporter.errorEx("Exception while initializing analysis listeners", e); @@ -404,7 +449,7 @@ public final class PmdAnalysis implements AutoCloseable { List rendererListeners = new ArrayList<>(renderers.size()); for (Renderer renderer : renderers) { try { - renderer.setFileNameRenderer(files().getFileNameRenderer()); + renderer.setFileNameRenderer(getFileNameRenderer()); @SuppressWarnings("PMD.CloseResource") GlobalAnalysisListener listener = Objects.requireNonNull(renderer.newListener(), "Renderer should provide non-null listener"); @@ -553,4 +598,59 @@ public final class PmdAnalysis implements AutoCloseable { + "https://pmd.github.io/{0}/pmd_userdocs_incremental_analysis.html", version); } } + + /** + * Return the textfile's display name. Takes the shortest path we + * can construct from the relativize roots. + * + *

package private for test only

+ */ + public static String getDisplayName(FileId file, List relativizeRoots) { + String best = file.toAbsolutePath(); + for (Path root : relativizeRoots) { + if (isFileSystemRoot(root)) { + // Absolutize the path. Since the relativize roots are + // sorted by ascending length, this should be the first in the list + // (so another root can override it). + best = file.toAbsolutePath(); + continue; + } + + String relative = relativizePath(root.toAbsolutePath().toString(), file.toAbsolutePath()); + if (countSegments(relative) < countSegments(best)) { + best = relative; + } + } + return best; + } + + private static int countSegments(String best) { + return StringUtils.countMatches(best, File.separatorChar); + } + + private static String relativizePath(String base, String other) { + String[] baseSegments = base.split("[/\\\\]"); + String[] otherSegments = other.split("[/\\\\]"); + int prefixLength = 0; + int maxi = Math.min(baseSegments.length, otherSegments.length); + while (prefixLength < maxi && baseSegments[prefixLength].equals(otherSegments[prefixLength])) { + prefixLength++; + } + + if (prefixLength == 0) { + return other; + } + + List relative = new ArrayList<>(); + for (int i = prefixLength; i < baseSegments.length; i++) { + relative.add(".."); + } + relative.addAll(Arrays.asList(otherSegments).subList(prefixLength, otherSegments.length)); + return String.join(File.separator, relative); + } + + /** Return whether the path is the root path (/). */ + private static boolean isFileSystemRoot(Path root) { + return root.isAbsolute() && root.getNameCount() == 0; + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileCollector.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileCollector.java index a2458737af..21a8a8d98e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileCollector.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileCollector.java @@ -5,7 +5,6 @@ package net.sourceforge.pmd.lang.document; import java.io.Closeable; -import java.io.File; import java.io.IOException; import java.net.URI; import java.nio.charset.Charset; @@ -22,7 +21,6 @@ import java.nio.file.ProviderNotFoundException; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.EnumSet; @@ -33,7 +31,6 @@ import java.util.List; import java.util.Objects; import java.util.Set; -import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,7 +41,6 @@ import net.sourceforge.pmd.internal.util.IOUtil; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.LanguageVersionDiscoverer; -import net.sourceforge.pmd.renderers.FileNameRenderer; import net.sourceforge.pmd.util.AssertionUtil; import net.sourceforge.pmd.util.log.MessageReporter; @@ -65,7 +61,6 @@ public final class FileCollector implements AutoCloseable { private final LanguageVersionDiscoverer discoverer; private final MessageReporter reporter; private final FileId outerFsPath; - private final List relativizeRootPaths = new ArrayList<>(); private boolean closed; // construction @@ -92,7 +87,6 @@ public final class FileCollector implements AutoCloseable { public FileCollector newCollector(MessageReporter logger) { FileCollector fileCollector = new FileCollector(discoverer, logger, null); fileCollector.charset = this.charset; - fileCollector.relativizeRootPaths.addAll(this.relativizeRootPaths); return fileCollector; } @@ -259,61 +253,6 @@ public final class FileCollector implements AutoCloseable { return true; } - /** - * Return the textfile's display name. Takes the shortest path we - * can construct from the relativize roots. - * - *

package private for test only

- */ - static String getDisplayName(FileId file, List relativizeRoots) { - String best = file.toAbsolutePath(); - for (Path root : relativizeRoots) { - if (isFileSystemRoot(root)) { - // Absolutize the path. Since the relativize roots are - // sorted by ascending length, this should be the first in the list - // (so another root can override it). - best = file.toAbsolutePath(); - continue; - } - - String relative = relativizePath(root.toAbsolutePath().toString(), file.toAbsolutePath()); - if (countSegments(relative) < countSegments(best)) { - best = relative; - } - } - return best; - } - - private static int countSegments(String best) { - return StringUtils.countMatches(best, File.separatorChar); - } - - private static String relativizePath(String base, String other) { - String[] baseSegments = base.split("[/\\\\]"); - String[] otherSegments = other.split("[/\\\\]"); - int prefixLength = 0; - int maxi = Math.min(baseSegments.length, otherSegments.length); - while (prefixLength < maxi && baseSegments[prefixLength].equals(otherSegments[prefixLength])) { - prefixLength++; - } - - if (prefixLength == 0) { - return other; - } - - List relative = new ArrayList<>(); - for (int i = prefixLength; i < baseSegments.length; i++) { - relative.add(".."); - } - relative.addAll(Arrays.asList(otherSegments).subList(prefixLength, otherSegments.length)); - return String.join(File.separator, relative); - } - - /** Return whether the path is the root path (/). */ - private static boolean isFileSystemRoot(Path root) { - return root.isAbsolute() && root.getNameCount() == 0; - } - /** * Add a directory recursively using {@link #addFile(Path)} on @@ -447,45 +386,6 @@ public final class FileCollector implements AutoCloseable { this.charset = Objects.requireNonNull(charset); } - /** - * Add a prefix that is used to relativize file paths as their display name. - * For instance, when adding a file {@code /tmp/src/main/java/org/foo.java}, - * and relativizing with {@code /tmp/src/}, the registered {@link TextFile} - * will have a path id of {@code /tmp/src/main/java/org/foo.java}, and a - * display name of {@code main/java/org/foo.java}. - * - *

This only matters for files added from a {@link Path} object. - * - * @param path Path with which to relativize - */ - public void relativizeWith(Path path) { - this.relativizeRootPaths.add(Objects.requireNonNull(path)); - this.relativizeRootPaths.sort(Comparator.comparingInt(Path::getNameCount).thenComparing(o -> o)); - } - - // todo doc - public FileNameRenderer getFileNameRenderer() { - return new FileNameRenderer() { - private final List relativizeRootPaths = new ArrayList<>(FileCollector.this.relativizeRootPaths); - - @Override - public String getDisplayName(FileId fileId) { - String localDisplayName = getLocalDisplayName(fileId); - if (fileId.getParentFsPath() != null) { - return getDisplayName(fileId.getParentFsPath()) + "!" + localDisplayName; - } - return localDisplayName; - } - - private String getLocalDisplayName(FileId file) { - if (!relativizeRootPaths.isEmpty()) { - return FileCollector.getDisplayName(file, relativizeRootPaths); - } - return file.getOriginalPath(); - } - }; - } - // filtering diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDFilelistTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDFilelistTest.java index 52826a7175..d61b9d552c 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDFilelistTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDFilelistTest.java @@ -87,13 +87,13 @@ class PMDFilelistTest { try (PmdAnalysis pmd = PmdAnalysis.create(conf)) { List files = pmd.files().getCollectedFiles(); assertThat(files, hasSize(2)); - assertHasName(files.get(0), IOUtil.normalizePath("net/sourceforge/pmd/cli/src/anotherfile.dummy"), pmd.files()); - assertHasName(files.get(1), IOUtil.normalizePath("net/sourceforge/pmd/cli/src/somefile.dummy"), pmd.files()); + assertHasName(files.get(0), IOUtil.normalizePath("net/sourceforge/pmd/cli/src/anotherfile.dummy"), pmd); + assertHasName(files.get(1), IOUtil.normalizePath("net/sourceforge/pmd/cli/src/somefile.dummy"), pmd); } } - public static void assertHasName(TextFile textFile, String expected, FileCollector files) { - assertThat(files.getFileNameRenderer().getDisplayName(textFile), equalTo(expected)); + public static void assertHasName(TextFile textFile, String expected, PmdAnalysis pmd) { + assertThat(pmd.getFileNameRenderer().getDisplayName(textFile), equalTo(expected)); } @Test @@ -104,9 +104,9 @@ class PMDFilelistTest { try (PmdAnalysis pmd = PmdAnalysis.create(conf)) { List files = pmd.files().getCollectedFiles(); assertThat(files, hasSize(3)); - assertHasName(files.get(0), ".." + IOUtil.normalizePath("/otherSrc/somefile.dummy"), pmd.files()); - assertHasName(files.get(1), "anotherfile.dummy", pmd.files()); - assertHasName(files.get(2), "somefile.dummy", pmd.files()); + assertHasName(files.get(0), ".." + IOUtil.normalizePath("/otherSrc/somefile.dummy"), pmd); + assertHasName(files.get(1), "anotherfile.dummy", pmd); + assertHasName(files.get(2), "somefile.dummy", pmd); } } @@ -119,9 +119,9 @@ class PMDFilelistTest { try (PmdAnalysis pmd = PmdAnalysis.create(conf)) { List files = pmd.files().getCollectedFiles(); assertThat(files, hasSize(3)); - assertHasName(files.get(0), "somefile.dummy", pmd.files()); - assertHasName(files.get(1), "anotherfile.dummy", pmd.files()); - assertHasName(files.get(2), "somefile.dummy", pmd.files()); + assertHasName(files.get(0), "somefile.dummy", pmd); + assertHasName(files.get(1), "anotherfile.dummy", pmd); + assertHasName(files.get(2), "somefile.dummy", pmd); } } @@ -133,9 +133,9 @@ class PMDFilelistTest { try (PmdAnalysis pmd = PmdAnalysis.create(conf)) { List files = pmd.files().getCollectedFiles(); assertThat(files, hasSize(3)); - assertHasName(files.get(0), RESOURCES.resolve("otherSrc/somefile.dummy").toAbsolutePath().toString(), pmd.files()); - assertHasName(files.get(1), RESOURCES.resolve("src/anotherfile.dummy").toAbsolutePath().toString(), pmd.files()); - assertHasName(files.get(2), RESOURCES.resolve("src/somefile.dummy").toAbsolutePath().toString(), pmd.files()); + assertHasName(files.get(0), RESOURCES.resolve("otherSrc/somefile.dummy").toAbsolutePath().toString(), pmd); + assertHasName(files.get(1), RESOURCES.resolve("src/anotherfile.dummy").toAbsolutePath().toString(), pmd); + assertHasName(files.get(2), RESOURCES.resolve("src/somefile.dummy").toAbsolutePath().toString(), pmd); } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cli/ZipFileTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cli/ZipFileTest.java index f2a8934081..03f6720cf9 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cli/ZipFileTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cli/ZipFileTest.java @@ -33,9 +33,9 @@ class ZipFileTest { try (PmdAnalysis pmd = PmdAnalysis.create(conf)) { List files = pmd.files().getCollectedFiles(); assertThat(files, hasSize(3)); - assertHasName(files.get(0), reportPath + "!/otherSrc/somefile.dummy", pmd.files()); - assertHasName(files.get(1), reportPath + "!/src/somefile.dummy", pmd.files()); - assertHasName(files.get(2), reportPath + "!/src/somefile1.dummy", pmd.files()); + assertHasName(files.get(0), reportPath + "!/otherSrc/somefile.dummy", pmd); + assertHasName(files.get(1), reportPath + "!/src/somefile.dummy", pmd); + assertHasName(files.get(2), reportPath + "!/src/somefile1.dummy", pmd); } } @@ -48,9 +48,9 @@ class ZipFileTest { List files = pmd.files().getCollectedFiles(); assertThat(files, hasSize(3)); String baseZipPath = IOUtil.normalizePath("net/sourceforge/pmd/cli/zipWithSources.zip"); - assertHasName(files.get(0), baseZipPath + "!/otherSrc/somefile.dummy", pmd.files()); - assertHasName(files.get(1), baseZipPath + "!/src/somefile.dummy", pmd.files()); - assertHasName(files.get(2), baseZipPath + "!/src/somefile1.dummy", pmd.files()); + assertHasName(files.get(0), baseZipPath + "!/otherSrc/somefile.dummy", pmd); + assertHasName(files.get(1), baseZipPath + "!/src/somefile.dummy", pmd); + assertHasName(files.get(2), baseZipPath + "!/src/somefile1.dummy", pmd); } } @@ -64,9 +64,9 @@ class ZipFileTest { try (PmdAnalysis pmd = PmdAnalysis.create(conf)) { List files = pmd.files().getCollectedFiles(); assertThat(files, hasSize(3)); - assertHasName(files.get(0), reportPath + "!/otherSrc/somefile.dummy", pmd.files()); - assertHasName(files.get(1), reportPath + "!/src/somefile.dummy", pmd.files()); - assertHasName(files.get(2), reportPath + "!/src/somefile1.dummy", pmd.files()); + assertHasName(files.get(0), reportPath + "!/otherSrc/somefile.dummy", pmd); + assertHasName(files.get(1), reportPath + "!/src/somefile.dummy", pmd); + assertHasName(files.get(2), reportPath + "!/src/somefile1.dummy", pmd); } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/FileCollectorTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/FileCollectorTest.java index 71fa4e30a3..a6b47cb18e 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/FileCollectorTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/FileCollectorTest.java @@ -18,6 +18,7 @@ import java.util.List; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; +import net.sourceforge.pmd.PmdAnalysis; import net.sourceforge.pmd.lang.DummyLanguageModule; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; @@ -94,7 +95,7 @@ class FileCollectorTest { @Test void testRelativize() { - String displayName = FileCollector.getDisplayName(FileId.forPath(Paths.get("a", "b", "c")), listOf(Paths.get("a"))); + String displayName = PmdAnalysis.getDisplayName(FileId.forPath(Paths.get("a", "b", "c")), listOf(Paths.get("a"))); assertEquals(displayName, Paths.get("b", "c").toString()); } @@ -120,8 +121,6 @@ class FileCollectorTest { private FileCollector newCollector(LanguageVersion forcedVersion) { LanguageVersionDiscoverer discoverer = new LanguageVersionDiscoverer(LanguageRegistry.PMD, forcedVersion); - FileCollector collector = FileCollector.newCollector(discoverer, new TestMessageReporter()); - collector.relativizeWith(tempFolder.toAbsolutePath()); - return collector; + return FileCollector.newCollector(discoverer, new TestMessageReporter()); } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/NioTextFileTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/NioTextFileTest.java index 087ab92f28..7c73f69feb 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/NioTextFileTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/NioTextFileTest.java @@ -16,8 +16,8 @@ import java.util.zip.ZipOutputStream; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import net.sourceforge.pmd.lang.LanguageRegistry; -import net.sourceforge.pmd.lang.LanguageVersionDiscoverer; +import net.sourceforge.pmd.PMDConfiguration; +import net.sourceforge.pmd.PmdAnalysis; class NioTextFileTest { @@ -34,14 +34,15 @@ class NioTextFileTest { zipOutputStream.closeEntry(); } - LanguageVersionDiscoverer discoverer = new LanguageVersionDiscoverer(LanguageRegistry.PMD, null); - try (FileCollector collector = FileCollector.newCollector(discoverer, new TestMessageReporter())) { - collector.addZipFileWithContent(zipArchive); - List collectedFiles = collector.getCollectedFiles(); + PMDConfiguration config = new PMDConfiguration(); + config.setReporter(new TestMessageReporter()); + try (PmdAnalysis pmd = PmdAnalysis.create(config)) { + pmd.files().addZipFileWithContent(zipArchive); + List collectedFiles = pmd.files().getCollectedFiles(); assertEquals(1, collectedFiles.size()); TextFile textFile = collectedFiles.get(0); assertEquals(zipArchive.toAbsolutePath() + "!/path/inside/someSource.dummy", - collector.getFileNameRenderer().getDisplayName(textFile)); + pmd.getFileNameRenderer().getDisplayName(textFile)); } } } diff --git a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt index 742022abbe..c2e9d003c1 100644 --- a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt +++ b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt @@ -11,7 +11,6 @@ import net.sourceforge.pmd.cpd.Tokenizer import net.sourceforge.pmd.cpd.Tokens import net.sourceforge.pmd.lang.ast.TokenMgrError import net.sourceforge.pmd.lang.document.FileId -import net.sourceforge.pmd.lang.document.TextFile import net.sourceforge.pmd.test.BaseTextComparisonTest import org.apache.commons.lang3.StringUtils import java.util.*