src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.java
branchJDK-8200758-branch
changeset 58696 61c44899b4eb
parent 58417 67ffaf3a2b75
child 58762 0fe62353385b
--- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.java	Fri Oct 18 11:00:57 2019 -0400
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.java	Fri Oct 18 14:14:37 2019 -0400
@@ -26,14 +26,19 @@
 package jdk.jpackage.internal;
 
 import java.io.*;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
 import java.nio.channels.FileChannel;
 import java.nio.file.FileVisitResult;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.SimpleFileVisitor;
 import java.nio.file.attribute.BasicFileAttributes;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.*;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
 
 /**
  * IOUtils
@@ -221,4 +226,105 @@
                     file.getAbsolutePath());
         }
     }
+
+    public static Path replaceSuffix(Path path, String suffix) {
+        Path parent = path.getParent();
+        String filename = path.getFileName().toString().replaceAll("\\.[^.]*$", "")
+                + Optional.ofNullable(suffix).orElse("");
+        return parent != null ? parent.resolve(filename) : Path.of(filename);
+    }
+
+    public static Path addSuffix(Path path, String suffix) {
+        Path parent = path.getParent();
+        String filename = path.getFileName().toString() + suffix;
+        return parent != null ? parent.resolve(filename) : Path.of(filename);
+    }
+
+    @FunctionalInterface
+    public static interface XmlConsumer {
+        void accept(XMLStreamWriter xml) throws IOException, XMLStreamException;
+    }
+
+    public static void createXml(Path dstFile, XmlConsumer xmlConsumer) throws
+            IOException {
+        XMLOutputFactory xmlFactory = XMLOutputFactory.newInstance();
+        try (Writer w = new BufferedWriter(new FileWriter(dstFile.toFile()))) {
+            // Wrap with pretty print proxy
+            XMLStreamWriter xml = (XMLStreamWriter) Proxy.newProxyInstance(
+                    XMLStreamWriter.class.getClassLoader(), new Class<?>[]{
+                XMLStreamWriter.class}, new PrettyPrintHandler(
+                    xmlFactory.createXMLStreamWriter(w)));
+
+            xml.writeStartDocument();
+            xmlConsumer.accept(xml);
+            xml.writeEndDocument();
+            xml.flush();
+            xml.close();
+        } catch (XMLStreamException ex) {
+            throw new IOException(ex);
+        } catch (IOException ex) {
+            throw ex;
+        }
+    }
+
+    private static class PrettyPrintHandler implements InvocationHandler {
+
+        PrettyPrintHandler(XMLStreamWriter target) {
+            this.target = target;
+        }
+
+        @Override
+        public Object invoke(Object proxy, Method method, Object[] args) throws
+                Throwable {
+            switch (method.getName()) {
+                case "writeStartElement":
+                    // update state of parent node
+                    if (depth > 0) {
+                        hasChildElement.put(depth - 1, true);
+                    }
+                    // reset state of current node
+                    hasChildElement.put(depth, false);
+                    // indent for current depth
+                    target.writeCharacters(EOL);
+                    target.writeCharacters(repeat(depth, INDENT));
+                    depth++;
+                    break;
+                case "writeEndElement":
+                    depth--;
+                    if (hasChildElement.get(depth) == true) {
+                        target.writeCharacters(EOL);
+                        target.writeCharacters(repeat(depth, INDENT));
+                    }
+                    break;
+                case "writeProcessingInstruction":
+                case "writeEmptyElement":
+                    // update state of parent node
+                    if (depth > 0) {
+                        hasChildElement.put(depth - 1, true);
+                    }
+                    // indent for current depth
+                    target.writeCharacters(EOL);
+                    target.writeCharacters(repeat(depth, INDENT));
+                    break;
+                default:
+                    break;
+            }
+            method.invoke(target, args);
+            return null;
+        }
+
+        private static String repeat(int d, String s) {
+            StringBuilder sb = new StringBuilder();
+            while (d-- > 0) {
+                sb.append(s);
+            }
+            return sb.toString();
+        }
+
+        private final XMLStreamWriter target;
+        private int depth = 0;
+        private final Map<Integer, Boolean> hasChildElement = new HashMap<>();
+        private static final String INDENT = "  ";
+        private static final String EOL = "\n";
+    }
 }