diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 1026fcf5..3b10e638 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -17,6 +17,8 @@ limitations under the License. == Unreleased +* Fixed Windows compatibility (for https://github.com/apache/logging-log4j-tools/issues/19[#19] by Piotr P. Karwasz, Volkan Yazıcı) + * Fixed unreleased directory order in `ChangelogExporter` (for https://github.com/apache/logging-log4j-tools/issues/17[#17] by Volkan Yazıcı) * Removed `security` as a change type from `log4j-changelog` (for https://github.com/apache/logging-log4j-tools/issues/14[#14] by Ralph Goers, Volkan Yazıcı) diff --git a/log4j-changelog/src/main/java/org/apache/logging/log4j/changelog/exporter/ChangelogExporter.java b/log4j-changelog/src/main/java/org/apache/logging/log4j/changelog/exporter/ChangelogExporter.java index 96ac8640..a2341d8b 100644 --- a/log4j-changelog/src/main/java/org/apache/logging/log4j/changelog/exporter/ChangelogExporter.java +++ b/log4j-changelog/src/main/java/org/apache/logging/log4j/changelog/exporter/ChangelogExporter.java @@ -16,6 +16,7 @@ */ package org.apache.logging.log4j.changelog.exporter; +import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Files; @@ -60,13 +61,13 @@ public static void performExport(final ChangelogExporterArgs args) { for (int releaseIndex = 0; releaseIndex < releaseDirectories.size(); releaseIndex++) { final Path releaseDirectory = releaseDirectories.get(releaseIndex); final ChangelogRelease changelogRelease = changelogReleases.get(releaseIndex); - final String releaseChangelogTemplateFile = ChangelogFiles.releaseChangelogTemplateFile(releaseDirectory, args.changelogDirectory); + final Path releaseChangelogTemplateFile = ChangelogFiles.releaseChangelogTemplateFile(releaseDirectory); try { exportRelease( args.outputDirectory, + args.changelogDirectory, releaseDirectory, changelogRelease, - args.changelogDirectory, releaseChangelogTemplateFile); } catch (final Exception error) { final String message = @@ -96,25 +97,23 @@ public static void performExport(final ChangelogExporterArgs args) { final Path upcomingReleaseDirectory = ChangelogFiles.unreleasedDirectory(args.changelogDirectory, upcomingReleaseVersionMajor); final ChangelogRelease upcomingRelease = upcomingRelease(upcomingReleaseVersionMajor); - final String upcomingReleaseChangelogTemplateFile = - ChangelogFiles.releaseChangelogTemplateFile(upcomingReleaseDirectory, args.changelogDirectory); + final Path upcomingReleaseChangelogTemplateFile = + ChangelogFiles.releaseChangelogTemplateFile(upcomingReleaseDirectory); System.out.format("exporting upcoming release directory: `%s`%n", upcomingReleaseDirectory); exportRelease( args.outputDirectory, + args.changelogDirectory, upcomingReleaseDirectory, upcomingRelease, - args.changelogDirectory, upcomingReleaseChangelogTemplateFile); changelogReleases.add(upcomingRelease); }); // Export the release index - final String changelogIndexTemplateFile = ChangelogFiles.indexTemplateFile(args.changelogDirectory, args.changelogDirectory); exportIndex( args.outputDirectory, - changelogReleases, args.changelogDirectory, - changelogIndexTemplateFile); + changelogReleases); } @@ -139,13 +138,19 @@ private static boolean isNonEmptyDirectory(final Path path) { private static void exportRelease( final Path outputDirectory, + final Path changelogDirectory, final Path releaseDirectory, final ChangelogRelease changelogRelease, - final Path templateDirectory, - final String releaseChangelogTemplateFile) { - final Map> changelogEntriesByType = readChangelogEntriesByType(releaseDirectory); + final Path releaseChangelogTemplateFile) { + final Map> changelogEntriesByType = + readChangelogEntriesByType(releaseDirectory); try { - exportRelease(outputDirectory, changelogRelease, changelogEntriesByType, templateDirectory, releaseChangelogTemplateFile); + exportRelease( + outputDirectory, + changelogDirectory, + changelogRelease, + changelogEntriesByType, + releaseChangelogTemplateFile); } catch (final IOException error) { final String message = String.format("failed exporting release from directory `%s`", releaseDirectory); throw new UncheckedIOException(message, error); @@ -167,19 +172,20 @@ private static Map> readChangelogEntri private static void exportRelease( final Path outputDirectory, + final Path changelogDirectory, final ChangelogRelease release, final Map> entriesByType, - final Path templateDirectory, - final String releaseChangelogTemplateFile) + final Path releaseChangelogTemplateFile) throws IOException { final String releaseChangelogFileName = releaseChangelogFileName(release); final Path releaseChangelogFile = outputDirectory.resolve(releaseChangelogFileName); final Map releaseChangelogTemplateData = new LinkedHashMap<>(); releaseChangelogTemplateData.put("release", release); releaseChangelogTemplateData.put("entriesByType", entriesByType); + final String releaseChangelogTemplateName = templateName(changelogDirectory, releaseChangelogTemplateFile); FreeMarkerUtils.render( - templateDirectory, - releaseChangelogTemplateFile, + changelogDirectory, + releaseChangelogTemplateName, releaseChangelogTemplateData, releaseChangelogFile); } @@ -191,9 +197,8 @@ private static ChangelogRelease upcomingRelease(final int versionMajor) { private static void exportIndex( final Path outputDirectory, - final List changelogReleases, - final Path templateDirectory, - final String indexTemplateFile) { + final Path changelogDirectory, + final List changelogReleases) { final Object indexTemplateData = Collections.singletonMap( "releases", IntStream .range(0, changelogReleases.size()) @@ -208,8 +213,10 @@ private static void exportIndex( return (Object) changelogReleaseData; }) .collect(Collectors.toList())); + final Path indexTemplateFile = ChangelogFiles.indexTemplateFile(changelogDirectory); + final String indexTemplateName = templateName(changelogDirectory, indexTemplateFile); final Path indexFile = outputDirectory.resolve("index.adoc"); - FreeMarkerUtils.render(templateDirectory, indexTemplateFile, indexTemplateData, indexFile); + FreeMarkerUtils.render(changelogDirectory, indexTemplateName, indexTemplateData, indexFile); } private static String releaseChangelogFileName(final ChangelogRelease changelogRelease) { @@ -217,4 +224,17 @@ private static String releaseChangelogFileName(final ChangelogRelease changelogR return String.format("%s.adoc", changelogRelease.version); } + /** + * Creates a FreeMarker template name from the given path, assuming that the provided changelog directory is the template folder. + *

+ * {@link freemarker.cache.FileTemplateLoader} works against a template folder, hence the path relativization required. + *

+ */ + private static String templateName(final Path changelogDirectory, final Path path) { + final Path relativePath = changelogDirectory.relativize(path); + return File.pathSeparatorChar == '/' + ? relativePath.toString() + : relativePath.toString().replace('\\', '/'); + } + } diff --git a/log4j-changelog/src/main/java/org/apache/logging/log4j/changelog/exporter/FreeMarkerUtils.java b/log4j-changelog/src/main/java/org/apache/logging/log4j/changelog/exporter/FreeMarkerUtils.java index eaaa6378..5ab86d6e 100644 --- a/log4j-changelog/src/main/java/org/apache/logging/log4j/changelog/exporter/FreeMarkerUtils.java +++ b/log4j-changelog/src/main/java/org/apache/logging/log4j/changelog/exporter/FreeMarkerUtils.java @@ -27,28 +27,25 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import freemarker.cache.FileTemplateLoader; -import freemarker.template.Configuration; -import freemarker.template.DefaultObjectWrapper; -import freemarker.template.DefaultObjectWrapperBuilder; -import freemarker.template.Template; -import freemarker.template.TemplateExceptionHandler; +import freemarker.template.*; final class FreeMarkerUtils { private FreeMarkerUtils() {} @SuppressFBWarnings("DMI_HARDCODED_ABSOLUTE_FILENAME") - private static Configuration createConfiguration(final Path root) { - final Configuration configuration = new Configuration(Configuration.VERSION_2_3_29); + private static Configuration createConfiguration(final Path templateDirectory) { + final Version configurationVersion = Configuration.VERSION_2_3_29; + final Configuration configuration = new Configuration(configurationVersion); configuration.setDefaultEncoding(CharsetUtils.CHARSET_NAME); configuration.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); try { - configuration.setTemplateLoader(new FileTemplateLoader(root.toFile())); + configuration.setTemplateLoader(new FileTemplateLoader(templateDirectory.toFile())); } catch (final IOException error) { throw new UncheckedIOException(error); } final DefaultObjectWrapperBuilder objectWrapperBuilder = - new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_27); + new DefaultObjectWrapperBuilder(configurationVersion); objectWrapperBuilder.setExposeFields(true); final DefaultObjectWrapper objectWrapper = objectWrapperBuilder.build(); configuration.setObjectWrapper(objectWrapper); @@ -61,12 +58,12 @@ private static Configuration createConfiguration(final Path root) { @SuppressFBWarnings("TEMPLATE_INJECTION_FREEMARKER") static void render( final Path templateDirectory, - final String templateFile, + final String templateName, final Object templateData, final Path outputFile) { try { final Configuration configuration = createConfiguration(templateDirectory); - final Template template = configuration.getTemplate(templateFile); + final Template template = configuration.getTemplate(templateName); final Path outputFileParent = outputFile.getParent(); if (outputFileParent != null) { Files.createDirectories(outputFileParent); @@ -81,7 +78,7 @@ static void render( } catch (final Exception error) { final String message = String.format( "failed rendering template `%s` to file `%s`", - templateFile, + templateName, outputFile); throw new RuntimeException(message, error); } diff --git a/log4j-changelog/src/main/java/org/apache/logging/log4j/changelog/util/XmlWriter.java b/log4j-changelog/src/main/java/org/apache/logging/log4j/changelog/util/XmlWriter.java index d3f35b79..04503107 100644 --- a/log4j-changelog/src/main/java/org/apache/logging/log4j/changelog/util/XmlWriter.java +++ b/log4j-changelog/src/main/java/org/apache/logging/log4j/changelog/util/XmlWriter.java @@ -38,6 +38,8 @@ public final class XmlWriter { + private static final String LS = System.lineSeparator(); + private XmlWriter() {} public static void toFile( @@ -69,22 +71,21 @@ public static String toString(final String rootElementName, final BiConsumer<", "-->\n<") + .replace("?><", "-->" + LS + "<") .replaceFirst( - '<' + rootElementName + " (.+>\n)", - ('<' + rootElementName + " xmlns=\"" + XmlUtils.XML_NAMESPACE + "\"\n" + - padding + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" + - padding + "xsi:schemaLocation=\"" + XmlUtils.XML_NAMESPACE + " "+ XmlUtils.XML_SCHEMA_LOCATION + "\"\n" + + '<' + rootElementName + " (.+>" + LS + ")", + ('<' + rootElementName + " xmlns=\"" + XmlUtils.XML_NAMESPACE + "\"" + LS + + padding + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" + LS + + padding + "xsi:schemaLocation=\"" + XmlUtils.XML_NAMESPACE + " "+ XmlUtils.XML_SCHEMA_LOCATION + "\"" + LS + padding + "$1")); } diff --git a/log4j-changelog/src/test/java/org/apache/logging/log4j/changelog/FileTestUtils.java b/log4j-changelog/src/test/java/org/apache/logging/log4j/changelog/FileTestUtils.java index d62ea229..d5a9a388 100644 --- a/log4j-changelog/src/test/java/org/apache/logging/log4j/changelog/FileTestUtils.java +++ b/log4j-changelog/src/test/java/org/apache/logging/log4j/changelog/FileTestUtils.java @@ -20,7 +20,9 @@ import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.*; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream;