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");
+        });
+    }
+}

Reply via email to