This is an automated email from the ASF dual-hosted git repository. kwin pushed a commit to branch feature/support-git-mv in repository https://gitbox.apache.org/repos/asf/maven-doxia-converter.git
commit 197088c3c50d9015f5d44e366d856f10f67a891c Author: Konrad Windszus <k...@apache.org> AuthorDate: Mon Nov 11 19:13:16 2024 +0100 [DOXIATOOLS-90] Optionally post-process output with 'git mv' This allows to keep the git history of converted input files (in case those are part of a git repository) --- .../java/org/apache/maven/doxia/Converter.java | 8 +++ .../org/apache/maven/doxia/DefaultConverter.java | 63 +++++++++++++++++++--- .../org/apache/maven/doxia/cli/CLIManager.java | 10 +++- .../org/apache/maven/doxia/cli/ConverterCli.java | 21 ++++++-- .../maven/doxia/wrapper/InputFileWrapper.java | 36 ++----------- .../apache/maven/doxia/DefaultConverterTest.java | 40 ++++++++++++++ 6 files changed, 133 insertions(+), 45 deletions(-) diff --git a/src/main/java/org/apache/maven/doxia/Converter.java b/src/main/java/org/apache/maven/doxia/Converter.java index 360632a..63dae08 100644 --- a/src/main/java/org/apache/maven/doxia/Converter.java +++ b/src/main/java/org/apache/maven/doxia/Converter.java @@ -29,6 +29,12 @@ import org.apache.maven.doxia.wrapper.OutputStreamWrapper; * @author <a href="mailto:vincent.sive...@gmail.com">Vincent Siveton</a> */ public interface Converter { + enum PostProcess { + NONE, + REMOVE_AFTER_CONVERSION, + GIT_MV_INPUT_TO_OUTPUT, + } + /** * @param input an input file wrapper, not null. * @param output an output file wrapper, not null. @@ -55,4 +61,6 @@ public interface Converter { * @param formatOutput <code>true</code> to format the generated files, <code>false</code> otherwise. */ void setFormatOutput(boolean formatOutput); + + void setPostProcess(PostProcess postProcess); } diff --git a/src/main/java/org/apache/maven/doxia/DefaultConverter.java b/src/main/java/org/apache/maven/doxia/DefaultConverter.java index 250d968..313cb4f 100644 --- a/src/main/java/org/apache/maven/doxia/DefaultConverter.java +++ b/src/main/java/org/apache/maven/doxia/DefaultConverter.java @@ -30,12 +30,15 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; +import java.util.Scanner; import com.ibm.icu.text.CharsetDetector; import com.ibm.icu.text.CharsetMatch; @@ -204,6 +207,8 @@ public class DefaultConverter implements Converter { } } + private PostProcess postProcess = PostProcess.NONE; + /** Flag to format the generated files, actually only for XML based sinks. */ private boolean formatOutput; @@ -229,7 +234,6 @@ public class DefaultConverter implements Converter { try { if (input.getFile().isFile()) { convert(input.getFile(), input.getEncoding(), input.getFormat(), output); - cleanUp(input, input.getFile()); } else { List<File> files; try { @@ -250,7 +254,6 @@ public class DefaultConverter implements Converter { File relativeOutputDirectory = new File( PathTool.getRelativeFilePath(input.getFile().getAbsolutePath(), f.getParent())); convert(f, input.getEncoding(), input.getFormat(), output, relativeOutputDirectory); - cleanUp(input, f); } } } finally { @@ -258,12 +261,44 @@ public class DefaultConverter implements Converter { } } - private void cleanUp(InputFileWrapper input, File f) { - if (input.cleanUp(f)) { - LOGGER.info("Removed input file \"{}\" after successful conversion", f); + private void postProcessFiles(File inputFile, File outputFile) throws IOException, InterruptedException { + switch (postProcess) { + case REMOVE_AFTER_CONVERSION: + Files.delete(inputFile.toPath()); + LOGGER.info("Removed input file \"{}\" after successful conversion", inputFile); + break; + case GIT_MV_INPUT_TO_OUTPUT: + // first replace input by (converted) output file + Files.move(outputFile.toPath(), inputFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + // then rename input file to have the proper extension + executeCommand(String.format("git mv %s %s", inputFile, outputFile)); + LOGGER.info("Git moved \"{}\" to \"{}\"", inputFile, outputFile); + break; + default: + break; + } + } + + static void executeCommand(String command) throws IOException, InterruptedException { + Process process = Runtime.getRuntime().exec(command); + int exitCode = process.waitFor(); + if (exitCode != 0) { + logOutput(process.getInputStream(), ""); + logOutput(process.getErrorStream(), "Error: "); + throw new IOException("Command " + command + " failed with exit code " + exitCode); } } + static void logOutput(InputStream inputStream, String prefix) { + new Thread(() -> { + Scanner scanner = new Scanner(inputStream, "UTF-8"); + while (scanner.hasNextLine()) { + LOGGER.error("{}{}", prefix, scanner.nextLine()); + } + scanner.close(); + }).start(); + } + /** {@inheritDoc} */ @Override public void convert(InputReaderWrapper input, OutputStreamWrapper output) @@ -313,6 +348,11 @@ public class DefaultConverter implements Converter { this.formatOutput = formatOutput; } + @Override + public void setPostProcess(PostProcess postProcess) { + this.postProcess = postProcess; + } + // ---------------------------------------------------------------------- // Private methods // ---------------------------------------------------------------------- @@ -336,10 +376,11 @@ public class DefaultConverter implements Converter { * @param parserFormat a not null supported format * @param output not null OutputFileWrapper object * @param relativeOutputDirectory the relative output directory (may be null, created if it does not exist yet) + * @return the output file * @throws ConverterException if any * @throws UnsupportedFormatException if any */ - private void convert( + private File convert( File inputFile, String inputEncoding, DoxiaFormat parserFormat, @@ -438,6 +479,16 @@ public class DefaultConverter implements Converter { "Successfully converted file \"{}\" to \"{}\"", inputFile.getAbsolutePath(), outputFile.getAbsolutePath()); + try { + postProcessFiles(inputFile, outputFile); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new ConverterException("Error post processing files: " + e.getMessage(), e); + } + catch (IOException e) { + throw new ConverterException("Error post processing files: " + e.getMessage(), e); + } + return outputFile; } /** diff --git a/src/main/java/org/apache/maven/doxia/cli/CLIManager.java b/src/main/java/org/apache/maven/doxia/cli/CLIManager.java index e8a0759..8818060 100644 --- a/src/main/java/org/apache/maven/doxia/cli/CLIManager.java +++ b/src/main/java/org/apache/maven/doxia/cli/CLIManager.java @@ -28,6 +28,7 @@ import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; +import org.apache.maven.doxia.Converter; import org.apache.maven.doxia.DefaultConverter; import static org.apache.commons.lang3.StringUtils.join; @@ -49,6 +50,8 @@ class CLIManager { static final String REMOVE_IN = "removeIn"; + static final String GIT_MV_INPUT_TO_OUTPUT = "gitMvInputToOutput"; + /** out String */ static final String OUT = "out"; @@ -99,6 +102,9 @@ class CLIManager { .longOpt("removeInputAfterConversion") .desc("Whether to remove the input file(s) after successful conversion") .build()); + OPTIONS.addOption(Option.builder(GIT_MV_INPUT_TO_OUTPUT) + .desc("When this flag is set the output file(s) is(are) written to the input file(s) and afterwards renamed to have the proper extension with 'git mv'") + .build()); OPTIONS.addOption(Option.builder(OUT) .longOpt("output") .desc("Output file or directory.") @@ -160,11 +166,11 @@ class CLIManager { private static String getSupportedFormat() { String fromFormats = EnumSet.allOf(DefaultConverter.DoxiaFormat.class).stream() - .filter(DefaultConverter.DoxiaFormat::hasParser) + .filter(Converter.DoxiaFormat::hasParser) .map(f -> f.toString().toLowerCase()) .collect(Collectors.joining(", ")); String toFormats = EnumSet.allOf(DefaultConverter.DoxiaFormat.class).stream() - .filter(DefaultConverter.DoxiaFormat::hasSink) + .filter(Converter.DoxiaFormat::hasSink) .map(f -> f.toString().toLowerCase()) .collect(Collectors.joining(", ")); return EOL + "Supported Formats:" + EOL + " from: " + fromFormats diff --git a/src/main/java/org/apache/maven/doxia/cli/ConverterCli.java b/src/main/java/org/apache/maven/doxia/cli/ConverterCli.java index 3ad605b..579e1d8 100644 --- a/src/main/java/org/apache/maven/doxia/cli/ConverterCli.java +++ b/src/main/java/org/apache/maven/doxia/cli/ConverterCli.java @@ -31,6 +31,7 @@ import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.ParseException; import org.apache.commons.lang3.reflect.FieldUtils; import org.apache.maven.doxia.Converter; +import org.apache.maven.doxia.Converter.PostProcess; import org.apache.maven.doxia.ConverterException; import org.apache.maven.doxia.DefaultConverter; import org.apache.maven.doxia.UnsupportedFormatException; @@ -114,26 +115,35 @@ public class ConverterCli { InputFileWrapper input; OutputFileWrapper output; + final PostProcess postProcess; try { String sourceFormat = commandLine.getOptionValue(CLIManager.FROM, CLIManager.AUTO_FORMAT); final DefaultConverter.DoxiaFormat parserFormat; if (CLIManager.AUTO_FORMAT.equalsIgnoreCase(sourceFormat)) { File inputFile = new File(commandLine.getOptionValue(CLIManager.IN)); - parserFormat = DefaultConverter.DoxiaFormat.autoDetectFormat(inputFile); + parserFormat = Converter.DoxiaFormat.autoDetectFormat(inputFile); if (debug) { System.out.println("Auto detected input format: " + parserFormat); } } else { - parserFormat = DefaultConverter.DoxiaFormat.valueOf(sourceFormat.toUpperCase()); + parserFormat = Converter.DoxiaFormat.valueOf(sourceFormat.toUpperCase()); } String targetFormat = commandLine.getOptionValue(CLIManager.TO); + if (commandLine.hasOption(CLIManager.REMOVE_IN) && commandLine.hasOption(CLIManager.GIT_MV_INPUT_TO_OUTPUT)) { + throw new IllegalArgumentException("Options 'removeIn' and 'gitMvInputToOutput' are mutually exclusive."); + } else if (commandLine.hasOption(CLIManager.REMOVE_IN)) { + postProcess = PostProcess.REMOVE_AFTER_CONVERSION; + } else if (commandLine.hasOption(CLIManager.GIT_MV_INPUT_TO_OUTPUT)) { + postProcess = PostProcess.GIT_MV_INPUT_TO_OUTPUT; + } else { + postProcess = PostProcess.NONE; + } final DefaultConverter.DoxiaFormat sinkFormat = - DefaultConverter.DoxiaFormat.valueOf(targetFormat.toUpperCase()); + Converter.DoxiaFormat.valueOf(targetFormat.toUpperCase()); input = InputFileWrapper.valueOf( commandLine.getOptionValue(CLIManager.IN), parserFormat, - commandLine.getOptionValue(CLIManager.INENCODING), - commandLine.hasOption(CLIManager.REMOVE_IN)); + commandLine.getOptionValue(CLIManager.INENCODING)); output = OutputFileWrapper.valueOf( commandLine.getOptionValue(CLIManager.OUT), sinkFormat, @@ -152,6 +162,7 @@ public class ConverterCli { boolean format = commandLine.hasOption(CLIManager.FORMAT); converter.setFormatOutput(format); + converter.setPostProcess(postProcess); try { converter.convert(input, output); diff --git a/src/main/java/org/apache/maven/doxia/wrapper/InputFileWrapper.java b/src/main/java/org/apache/maven/doxia/wrapper/InputFileWrapper.java index 2b20c1c..3722122 100644 --- a/src/main/java/org/apache/maven/doxia/wrapper/InputFileWrapper.java +++ b/src/main/java/org/apache/maven/doxia/wrapper/InputFileWrapper.java @@ -36,11 +36,6 @@ public class InputFileWrapper extends AbstractFileWrapper { private final DefaultConverter.DoxiaFormat format; - /** - * If true, the related input file(s) will be removed after the conversion. - */ - private final boolean removeAfterConversion; - /** * Private constructor. * @@ -50,13 +45,11 @@ public class InputFileWrapper extends AbstractFileWrapper { * @throws UnsupportedEncodingException if the encoding is unsupported. * @throws FileNotFoundException if the file for absolutePath is not found. */ - private InputFileWrapper( - String absolutePath, DefaultConverter.DoxiaFormat format, String charsetName, boolean removeAfterConversion) + private InputFileWrapper(String absolutePath, DefaultConverter.DoxiaFormat format, String charsetName) throws UnsupportedEncodingException, FileNotFoundException { super(absolutePath, charsetName); this.format = format; - this.removeAfterConversion = removeAfterConversion; if (!getFile().exists()) { throw new FileNotFoundException("The file '" + getFile().getAbsolutePath() + "' doesn't exist."); } @@ -72,42 +65,21 @@ public class InputFileWrapper extends AbstractFileWrapper { */ public static InputFileWrapper valueOf(String absolutePath, DefaultConverter.DoxiaFormat format) throws UnsupportedEncodingException, FileNotFoundException { - return valueOf(absolutePath, format, WriterFactory.UTF_8, false); + return valueOf(absolutePath, format, WriterFactory.UTF_8); } - /** - * Performs potential clean up operations on the given file - * @param file the file to clean up, not null. - * @return {@code true} if the file was removed. - */ - public boolean cleanUp(File file) { - if (!file.toString().startsWith(getFile().toString())) { - throw new IllegalStateException( - "The given file " + file + " is not related to the input file: " + getFile()); - } - if (removeAfterConversion) { - return file.delete(); - } - return false; - } - - public static InputFileWrapper valueOf(String absolutePath, DefaultConverter.DoxiaFormat format, String charsetName) - throws UnsupportedEncodingException, FileNotFoundException { - return valueOf(absolutePath, format, charsetName, false); - } /** * @param absolutePath for a wanted file or a wanted directory, not null. * @param format not null * @param charsetName could be null - * @param removeAfterConversion if true, the file will be removed after the conversion * @return a type safe input reader * @throws UnsupportedEncodingException if the encoding is unsupported. * @throws FileNotFoundException if the file for absolutePath is not found. */ public static InputFileWrapper valueOf( - String absolutePath, DefaultConverter.DoxiaFormat format, String charsetName, boolean removeAfterConversion) + String absolutePath, DefaultConverter.DoxiaFormat format, String charsetName) throws UnsupportedEncodingException, FileNotFoundException { - return new InputFileWrapper(absolutePath, format, charsetName, removeAfterConversion); + return new InputFileWrapper(absolutePath, format, charsetName); } public DefaultConverter.DoxiaFormat getFormat() { diff --git a/src/test/java/org/apache/maven/doxia/DefaultConverterTest.java b/src/test/java/org/apache/maven/doxia/DefaultConverterTest.java new file mode 100644 index 0000000..a4abe28 --- /dev/null +++ b/src/test/java/org/apache/maven/doxia/DefaultConverterTest.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.doxia; + +import java.io.IOException; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +class DefaultConverterTest { + + @Test + void testExecuteCommand() throws IOException, InterruptedException { + DefaultConverter.executeCommand("git version"); + } + + @Test + void testExecuteInvalidCommand() throws IOException, InterruptedException { + assertThrows(IOException.class, () -> { + DefaultConverter.executeCommand("invalid command"); + }); + } +}