From 2534caf7fdad8a1064618ab16abd4d3d89b4af66 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 14 Jul 2022 09:33:51 +0200 Subject: [PATCH] Polishing. Add missing line breaks after XML declaration and before the file end. See #218 --- .../release/build/BuildConfiguration.java | 4 +- .../data/release/build/MavenBuildSystem.java | 74 +++++++++++++++---- .../build/MavenBuildSystemUnitTests.java | 52 +++++++++++++ 3 files changed, 112 insertions(+), 18 deletions(-) create mode 100644 src/test/java/org/springframework/data/release/build/MavenBuildSystemUnitTests.java diff --git a/src/main/java/org/springframework/data/release/build/BuildConfiguration.java b/src/main/java/org/springframework/data/release/build/BuildConfiguration.java index 32ade65..17eb9b0 100644 --- a/src/main/java/org/springframework/data/release/build/BuildConfiguration.java +++ b/src/main/java/org/springframework/data/release/build/BuildConfiguration.java @@ -22,7 +22,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.data.release.model.Project; import org.springframework.plugin.core.OrderAwarePluginRegistry; import org.springframework.plugin.core.PluginRegistry; -import org.xmlbeam.ProjectionFactory; + import org.xmlbeam.XBProjector; import org.xmlbeam.XBProjector.Flags; import org.xmlbeam.config.DefaultXMLFactoriesConfig; @@ -42,7 +42,7 @@ class BuildConfiguration { } @Bean - public ProjectionFactory projectionFactory() { + public XBProjector projectionFactory() { DefaultXMLFactoriesConfig config = new DefaultXMLFactoriesConfig(); config.setNamespacePhilosophy(NamespacePhilosophy.AGNOSTIC); diff --git a/src/main/java/org/springframework/data/release/build/MavenBuildSystem.java b/src/main/java/org/springframework/data/release/build/MavenBuildSystem.java index 880d77f..c108886 100644 --- a/src/main/java/org/springframework/data/release/build/MavenBuildSystem.java +++ b/src/main/java/org/springframework/data/release/build/MavenBuildSystem.java @@ -23,9 +23,23 @@ import lombok.AccessLevel; import lombok.RequiredArgsConstructor; import lombok.experimental.FieldDefaults; +import java.io.BufferedInputStream; import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; import java.util.List; import java.util.function.Consumer; +import java.util.regex.Pattern; + +import javax.xml.transform.TransformerException; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import org.apache.commons.io.IOUtils; import org.springframework.core.annotation.Order; import org.springframework.data.release.build.CommandLine.Argument; @@ -48,7 +62,9 @@ import org.springframework.stereotype.Component; import org.springframework.util.Assert; import org.xmlbeam.ProjectionFactory; -import org.xmlbeam.io.XBFileIO; +import org.xmlbeam.XBProjector; +import org.xmlbeam.dom.DOMAccess; +import org.xmlbeam.io.XBStreamInput; /** * @author Oliver Gierke @@ -96,7 +112,7 @@ class MavenBuildSystem implements BuildSystem { updateBom(information, "bom/pom.xml", BOM); } else { - execute(workspace.getFile(POM_XML, updater.getProject()), pom -> { + doWithProjection(workspace.getFile(POM_XML, updater.getProject()), pom -> { updater.updateDependencyProperties(pom); updater.updateParentVersion(pom); @@ -156,7 +172,7 @@ class MavenBuildSystem implements BuildSystem { logger.log(BUILD, "Updating BOM pom.xml…"); - execute(workspace.getFile(file, project), pom -> { + doWithProjection(workspace.getFile(file, project), pom -> { for (ModuleIteration module : iteration.getModulesExcept(BUILD, BOM)) { @@ -197,7 +213,7 @@ class MavenBuildSystem implements BuildSystem { private void updateParentPom(UpdateInformation information) { // Fix version of shared resources to to-be-released version. - execute(workspace.getFile("parent/pom.xml", BUILD), ParentPom.class, pom -> { + doWithProjection(workspace.getFile("parent/pom.xml", BUILD), ParentPom.class, pom -> { logger.log(BUILD, "Setting shared resources version to %s.", information.getParentVersionToSet()); pom.setSharedResourcesVersion(information.getParentVersionToSet()); @@ -398,25 +414,51 @@ class MavenBuildSystem implements BuildSystem { return workspace.getFile(POM_XML, project).exists(); } - private void execute(File file, Consumer callback) { - execute(file, Pom.class, callback); + private void doWithProjection(File file, Consumer callback) { + doWithProjection(file, Pom.class, callback); } /** * TODO: Move XML file callbacks using the {@link ProjectionFactory} to {@link Workspace}. */ - private void execute(File file, Class type, Consumer callback) { + private void doWithProjection(File file, Class type, Consumer callback) { - XBFileIO io = projectionFactory.io().file(file); + try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))) { + byte[] content = doWithProjection((XBProjector) projectionFactory, bis, type, callback); - try { - - T pom = (T) io.read(type); - callback.accept(pom); - io.write(pom); - - } catch (Exception o_O) { - throw new RuntimeException(o_O); + try (FileOutputStream fos = new FileOutputStream(file)) { + fos.write(content); + } + } catch (IOException e) { + throw new RuntimeException(e); } } + + static byte[] doWithProjection(XBProjector projector, InputStream stream, Class type, + Consumer callback) throws IOException { + + XBStreamInput io = projector.io().stream(stream); + T pom = io.read(type); + callback.accept(pom); + + StringWriter writer = new StringWriter(); + try { + projector.config().createTransformer().transform(new DOMSource(((DOMAccess) pom).getDOMNode()), + new StreamResult(writer)); + } catch (TransformerException e) { + throw new RuntimeException(e); + } + + String s = writer.toString(); + + if (s.contains("standalone=\"no\"?><")) { + s = s.replaceAll(Pattern.quote("standalone=\"no\"?><"), "standalone=\"no\"?>" + IOUtils.LINE_SEPARATOR + "<"); + } + + if (!s.endsWith(IOUtils.LINE_SEPARATOR)) { + s += IOUtils.LINE_SEPARATOR; + } + + return s.getBytes(StandardCharsets.UTF_8); + } } diff --git a/src/test/java/org/springframework/data/release/build/MavenBuildSystemUnitTests.java b/src/test/java/org/springframework/data/release/build/MavenBuildSystemUnitTests.java new file mode 100644 index 0000000..314d7a7 --- /dev/null +++ b/src/test/java/org/springframework/data/release/build/MavenBuildSystemUnitTests.java @@ -0,0 +1,52 @@ +/* + * Copyright 2022 the original author or authors. + * + * 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 + * + * https://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 org.springframework.data.release.build; + +import static org.assertj.core.api.Assertions.*; + +import java.io.InputStream; + +import org.apache.commons.io.IOUtils; +import org.junit.jupiter.api.Test; + +import org.springframework.core.io.ClassPathResource; + +import org.xmlbeam.XBProjector; + +/** + * Unit tests for {@link MavenBuildSystem}. + * + * @author Mark Paluch + */ +class MavenBuildSystemUnitTests { + + XBProjector projector = new BuildConfiguration().projectionFactory(); + + @Test + void shouldAddLineBreaksAfterProcessing() throws Exception { + + ClassPathResource resource = new ClassPathResource("sample-pom.xml"); + + try (InputStream is = resource.getInputStream()) { + + byte[] bytes = MavenBuildSystem.doWithProjection(projector, is, Pom.class, pom -> {}); + + assertThat(new String(bytes)).contains("" + + IOUtils.LINE_SEPARATOR + "