From 30f349bdaf9cf612267647bd4328d091ba4c5214 Mon Sep 17 00:00:00 2001 From: Laurent SCHOELENS Date: Fri, 4 Aug 2023 09:52:38 +0200 Subject: [PATCH] Fixing JT-173 (backport of modified PR provided by phax) The goal of this fix is to ensure that the order of files specified in included configuration section is respected and used in plugin, no matter how plexus returns them by DirectoryScanner since order is not consistent. See issue https://github.com/codehaus-plexus/plexus-utils/issues/70 in plexus-utils --- .../org/jvnet/jaxb/maven/util/IOUtils.java | 89 +++++++++++- .../jvnet/jaxb/maven/util/IOUtilsTests.java | 129 ++++++++++++++++++ 2 files changed, 216 insertions(+), 2 deletions(-) create mode 100644 maven-plugin/plugin-core/src/test/java/org/jvnet/jaxb/maven/util/IOUtilsTests.java diff --git a/maven-plugin/plugin-core/src/main/java/org/jvnet/jaxb/maven/util/IOUtils.java b/maven-plugin/plugin-core/src/main/java/org/jvnet/jaxb/maven/util/IOUtils.java index 5b5b70ea1..7716a4609 100644 --- a/maven-plugin/plugin-core/src/main/java/org/jvnet/jaxb/maven/util/IOUtils.java +++ b/maven-plugin/plugin-core/src/main/java/org/jvnet/jaxb/maven/util/IOUtils.java @@ -6,11 +6,14 @@ import java.net.URI; import java.net.URL; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import org.codehaus.plexus.util.DirectoryScanner; +import org.codehaus.plexus.util.MatchPatterns; import org.codehaus.plexus.util.Scanner; +import org.codehaus.plexus.util.SelectorUtils; import org.jvnet.jaxb.maven.util.CollectionUtils.Function; import org.sonatype.plexus.build.incremental.BuildContext; import org.xml.sax.InputSource; @@ -95,11 +98,93 @@ public static List scanDirectoryForFiles(BuildContext buildContext, final scanner.scan(); - final List files = new ArrayList(); - for (final String name : scanner.getIncludedFiles()) { + // Reorder files according to includes specific file ordering + List orderedIncludedFiles = reorderFiles(scanner.getIncludedFiles(), includes); + + final List files = new ArrayList<>(); + for (final String name : orderedIncludedFiles) { files.add(new File(directory, name).getCanonicalFile()); } return files; } + + private static boolean isWildcard (final String s) { + return s.indexOf ('*') >= 0 || s.indexOf ('?') >= 0; + } + + /** + * Reorder the result of "scanner.getIncludedFile" so that the order of the + * source includes is maintained as good as possible. + * Examples:
+ * If the includes contain [a, b, c] and the resulting list should be in that order.
+ * If the includes contain [a, b*, c] the resulting list should be [a, matches-of(b*), c] + * + * @param resolvedFiles + * resolved files from scanner.getIncludedFiles. May not be null. + * @param includes + * The source includes in the correct order. May be null or empty. + * @return The ordered list of files, that tries to take the source order as good as possible + */ + protected static List reorderFiles(final String[] resolvedFiles, final String[] includes) { + if (includes == null || includes.length == 0) { + // return "as is" + return Arrays.asList(resolvedFiles); + } + + // copy of the initial list in order to avoid changes in resolvedFiles var + List resolvedFilesList = new ArrayList<>(Arrays.asList(resolvedFiles)); + final List ret = new ArrayList<>(resolvedFilesList.size()); + + // For each include + for (final String include : includes) { + // Create MatchPatterns from normalizePattern of the include (like it was done in DirectoryScanner) + MatchPatterns inc = MatchPatterns.from(normalizePattern(include)); + + // Find all matches in the resolved files list + final List matches = new ArrayList<>(); + for (final String resFile : resolvedFilesList) { + if (inc.matches(resFile, false)) { + matches.add(resFile); + } + } + + // Add all matches to the result list + ret.addAll(matches); + // Remove from the main list + resolvedFilesList.removeAll(matches); + } + + // Add all remaining resolved files in the order "as is" + ret.addAll(resolvedFilesList); + + return ret; + } + + /** + * Copy of Plexus-utils function (private) to normalize pattern of include.
+ * Normalizes the pattern, e.g. converts forward and backward slashes to the platform-specific file separator. + * + * @param pattern The pattern to normalize, must not be null. + * @return The normalized pattern, never null. + */ + private static String normalizePattern(String pattern) { + pattern = pattern.trim(); + + if (pattern.startsWith(SelectorUtils.REGEX_HANDLER_PREFIX)) { + if (File.separatorChar == '\\') { + pattern = org.codehaus.plexus.util.StringUtils.replace(pattern, "/", "\\\\"); + } else { + pattern = org.codehaus.plexus.util.StringUtils.replace(pattern, "\\\\", "/"); + } + } else { + pattern = pattern.replace(File.separatorChar == '/' ? '\\' : '/', File.separatorChar); + + if (pattern.endsWith(File.separator)) { + pattern += "**"; + } + } + + return pattern; + } } diff --git a/maven-plugin/plugin-core/src/test/java/org/jvnet/jaxb/maven/util/IOUtilsTests.java b/maven-plugin/plugin-core/src/test/java/org/jvnet/jaxb/maven/util/IOUtilsTests.java new file mode 100644 index 000000000..24cb39347 --- /dev/null +++ b/maven-plugin/plugin-core/src/test/java/org/jvnet/jaxb/maven/util/IOUtilsTests.java @@ -0,0 +1,129 @@ +package org.jvnet.jaxb.maven.util; + +import org.junit.Assert; +import org.junit.Test; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +public class IOUtilsTests { + + @Test + public void reorderFilesIncludesNull() { + String[] files = {"a.xsd", "c.xsd", "b.xsd" }; + List orderedFiles = IOUtils.reorderFiles(files, null); + + Assert.assertNotNull("Ordered files list should not be null", + orderedFiles); + Assert.assertEquals("Ordered files list should contains all elements of initial list", + files.length, orderedFiles.size()); + Assert.assertEquals(files[0], orderedFiles.get(0)); + Assert.assertEquals(files[1], orderedFiles.get(1)); + Assert.assertEquals(files[2], orderedFiles.get(2)); + } + + @Test + public void reorderFilesIncludesEmpty() { + String[] files = {"a.xsd", "c.xsd", "b.xsd" }; + List orderedFiles = IOUtils.reorderFiles(files, new String[] {}); + + Assert.assertNotNull("Ordered files list should not be null", + orderedFiles); + Assert.assertEquals("Ordered files list should contains all elements of initial list", + files.length, orderedFiles.size()); + Assert.assertEquals(files[0], orderedFiles.get(0)); + Assert.assertEquals(files[1], orderedFiles.get(1)); + Assert.assertEquals(files[2], orderedFiles.get(2)); + } + + @Test + public void reorderFilesIncludesNoWildcard() { + String[] files = {"a.xsd", "c.xsd", "b.xsd" }; + List orderedFiles = IOUtils.reorderFiles(files, new String[] { "b.xsd", "c.xsd", "a.xsd" }); + + Assert.assertNotNull("Ordered files list should not be null", + orderedFiles); + Assert.assertEquals("Ordered files list should contains all elements of initial list", + files.length, orderedFiles.size()); + Assert.assertEquals(files[2], orderedFiles.get(0)); + Assert.assertEquals(files[1], orderedFiles.get(1)); + Assert.assertEquals(files[0], orderedFiles.get(2)); + } + + @Test + public void reorderFilesIncludesNoWildcardWithCommonSuffix() { + String[] files = {"a.xsd", "b.xsd", "service-ab.xsd" }; + List orderedFiles = IOUtils.reorderFiles(files, new String[] { "b.xsd", "a.xsd", "service-ab.xsd" }); + + Assert.assertNotNull("Ordered files list should not be null", + orderedFiles); + Assert.assertEquals("Ordered files list should contains all elements of initial list", + files.length, orderedFiles.size()); + Assert.assertEquals(files[1], orderedFiles.get(0)); + Assert.assertEquals(files[0], orderedFiles.get(1)); + Assert.assertEquals(files[2], orderedFiles.get(2)); + } + + @Test + public void reorderFilesIncludesWithWildcardFirst() { + String[] files = {"a.xsd", "common/c1.xsd", "b.xsd", "common/c2.xsd", "common/a.xsd", "common/b.xsd" }; + List orderedFiles = IOUtils.reorderFiles(files, new String[] { "common/*.xsd", "a.xsd", "b.xsd" }); + + Assert.assertNotNull("Ordered files list should not be null", + orderedFiles); + Assert.assertEquals("Ordered files list should contains all elements of initial list", + files.length, orderedFiles.size()); + // we have all common/*.xsd files in same order + Assert.assertEquals(files[1], orderedFiles.get(0)); + Assert.assertEquals(files[3], orderedFiles.get(1)); + Assert.assertEquals(files[4], orderedFiles.get(2)); + Assert.assertEquals(files[5], orderedFiles.get(3)); + // and then a.xsd + Assert.assertEquals(files[0], orderedFiles.get(4)); + // and finally b.xsd + Assert.assertEquals(files[2], orderedFiles.get(5)); + } + + @Test + public void reorderFilesIncludesWithWildcardMiddle() { + String[] files = {"a.xsd", "common/c1.xsd", "b.xsd", "common/c2.xsd", "common/a.xsd", "common/b.xsd" }; + List orderedFiles = IOUtils.reorderFiles(files, new String[] { "a.xsd", "common/*.xsd", "b.xsd" }); + + Assert.assertNotNull("Ordered files list should not be null", + orderedFiles); + Assert.assertEquals("Ordered files list should contains all elements of initial list", + files.length, orderedFiles.size()); + + // we have a.xsd + Assert.assertEquals(files[0], orderedFiles.get(0)); + // and then all common/*.xsd files in same order + Assert.assertEquals(files[1], orderedFiles.get(1)); + Assert.assertEquals(files[3], orderedFiles.get(2)); + Assert.assertEquals(files[4], orderedFiles.get(3)); + Assert.assertEquals(files[5], orderedFiles.get(4)); + // and finally b.xsd + Assert.assertEquals(files[2], orderedFiles.get(5)); + } + + @Test + public void reorderFilesIncludesWithWildcardLast() { + String[] files = {"a.xsd", "common/c1.xsd", "b.xsd", "common/c2.xsd", "common/a.xsd", "common/b.xsd" }; + List orderedFiles = IOUtils.reorderFiles(files, new String[] { "a.xsd", "b.xsd", "common/*.xsd" }); + + Assert.assertNotNull("Ordered files list should not be null", + orderedFiles); + Assert.assertEquals("Ordered files list should contains all elements of initial list", + files.length, orderedFiles.size()); + + // we have a.xsd + Assert.assertEquals(files[0], orderedFiles.get(0)); + // and then b.xsd + Assert.assertEquals(files[2], orderedFiles.get(1)); + // and finally all common/*.xsd files in same order + Assert.assertEquals(files[1], orderedFiles.get(2)); + Assert.assertEquals(files[3], orderedFiles.get(3)); + Assert.assertEquals(files[4], orderedFiles.get(4)); + Assert.assertEquals(files[5], orderedFiles.get(5)); + } +}