From e32a78b7f9a0133555e0aa65ff437f1d775e36aa Mon Sep 17 00:00:00 2001
From: Greg DiCristofaro <gregd@basistech.com>
Date: Tue, 29 Aug 2023 20:58:21 -0400
Subject: [PATCH] using regex replace to minimize changeset

---
 .../autopsy/apiupdate/ModuleUpdates.java      | 232 +++++++++---------
 1 file changed, 113 insertions(+), 119 deletions(-)

diff --git a/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/ModuleUpdates.java b/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/ModuleUpdates.java
index 6deb8ef357..a103571f71 100644
--- a/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/ModuleUpdates.java
+++ b/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/ModuleUpdates.java
@@ -6,36 +6,25 @@
 
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileOutputStream;
 import java.io.IOException;
+import java.nio.file.Files;
 import java.text.MessageFormat;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.function.Function;
 import java.util.jar.Attributes;
-import java.util.jar.Manifest;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import java.util.logging.SimpleFormatter;
 import java.util.logging.StreamHandler;
+import java.util.regex.MatchResult;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.transform.Transformer;
 import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamResult;
-import javax.xml.xpath.XPath;
-import javax.xml.xpath.XPathConstants;
 import javax.xml.xpath.XPathExpressionException;
-import javax.xml.xpath.XPathFactory;
 import org.apache.commons.lang3.StringUtils;
-import org.w3c.dom.Document;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
 import org.xml.sax.SAXException;
 
 /**
@@ -51,10 +40,23 @@ public class ModuleUpdates {
     }
 
     private static final Pattern SPEC_REGEX = Pattern.compile("^\\s*((?<major>\\d*)\\.)?(?<minor>\\d*)(\\.(?<patch>\\d*))?\\s*$");
-    private static final String SPEC_KEY = "OpenIDE-Module-Specification-Version";
-    private static final String SPEC_PROPS_KEY = "spec.version.base";
-    private static final String IMPL_KEY = "OpenIDE-Module-Implementation-Version";
-    private static final String RELEASE_KEY = "OpenIDE-Module";
+
+    private static final Pattern PROJECT_PROPS_SPEC_REGEX = Pattern.compile("^\\s*spec\\.version\\.base\\s*=.*$", Pattern.MULTILINE);
+    private static final String PROJECT_PROPS_SPEC_REPLACE_FMT = "spec.version.base={0}";
+
+    private static final String MANIFEST_FIELD_REGEX_FMT = "^\\s*{0}\\s*:.*$";
+
+    private static final String MANIFEST_IMPL_KEY = "OpenIDE-Module-Implementation-Version";
+    private static final Pattern MANIFEST_IMPL_REGEX = Pattern.compile(MessageFormat.format(MANIFEST_FIELD_REGEX_FMT, MANIFEST_IMPL_KEY), Pattern.MULTILINE);
+    private static final String MANIFEST_IMPL_REPLACE_FMT = MANIFEST_IMPL_KEY + ": {0}";
+
+    private static final String MANIFEST_SPEC_KEY = "OpenIDE-Module-Specification-Version";
+    private static final Pattern MANIFEST_SPEC_REGEX = Pattern.compile(MessageFormat.format(MANIFEST_FIELD_REGEX_FMT, MANIFEST_SPEC_KEY), Pattern.MULTILINE);
+    private static final String MANIFEST_SPEC_REPLACE_FMT = MANIFEST_SPEC_KEY + ": {0}";
+
+    private static final String MANIFEST_RELEASE_KEY = "OpenIDE-Module";
+    private static final Pattern MANIFEST_RELEASE_REGEX = Pattern.compile(MessageFormat.format(MANIFEST_FIELD_REGEX_FMT, MANIFEST_RELEASE_KEY), Pattern.MULTILINE);
+    private static final String MANIFEST_RELEASE_REPLACE_FMT = MANIFEST_RELEASE_KEY + ": {0}";
 
     private static final Pattern RELEASE_REGEX = Pattern.compile("^\\s*(?<releaseName>.+?)(/(?<releaseNum>\\d*))?\\s*$");
 
@@ -126,15 +128,15 @@ private static int tryParse(String str, int defaultVal, String resourceForLoggin
 
     public static ModuleVersionNumbers getVersionsFromJar(File jarFile) throws IOException {
         Attributes manifest = ManifestLoader.loadFromJar(jarFile);
-        String spec = manifest.getValue(SPEC_KEY);
+        String spec = manifest.getValue(MANIFEST_SPEC_KEY);
         SemVer specSemVer = parseSemVer(spec, DEFAULT_SEMVER,
-                MessageFormat.format("{0} in manifest for {1}", SPEC_KEY, jarFile));
+                MessageFormat.format("{0} in manifest for {1}", MANIFEST_SPEC_KEY, jarFile));
 
-        int implementation = tryParse(manifest.getValue(IMPL_KEY), DEFAULT_VERS_VAL,
-                MessageFormat.format("{0} in manifest for {1}", IMPL_KEY, jarFile));
+        int implementation = tryParse(manifest.getValue(MANIFEST_IMPL_KEY), DEFAULT_VERS_VAL,
+                MessageFormat.format("{0} in manifest for {1}", MANIFEST_IMPL_KEY, jarFile));
 
-        ReleaseVal release = parseReleaseVers(manifest.getValue(RELEASE_KEY),
-                MessageFormat.format("{0} in manifest for {1}", RELEASE_KEY, jarFile));
+        ReleaseVal release = parseReleaseVers(manifest.getValue(MANIFEST_RELEASE_KEY),
+                MessageFormat.format("{0} in manifest for {1}", MANIFEST_RELEASE_KEY, jarFile));
 
         return new ModuleVersionNumbers(jarFile.getName(), specSemVer, implementation, release);
     }
@@ -160,7 +162,7 @@ private static Map<String, File> getModuleDirs(File srcDir) throws IOException {
             if (manifestFile.isFile()) {
                 try (FileInputStream manifestIs = new FileInputStream(manifestFile)) {
                     Attributes manifestAttrs = ManifestLoader.loadInputStream(manifestIs);
-                    ReleaseVal releaseVal = parseReleaseVers(manifestAttrs.getValue(RELEASE_KEY), manifestFile.getAbsolutePath());
+                    ReleaseVal releaseVal = parseReleaseVers(manifestAttrs.getValue(MANIFEST_RELEASE_KEY), manifestFile.getAbsolutePath());
                     moduleDirMapping.put(releaseVal.getModuleName(), dir);
                 }
             }
@@ -197,6 +199,18 @@ static void setVersions(File srcDir, Map<String, ModuleVersionNumbers> versNums)
         }
     }
 
+    private static String regexUpdate(Pattern pattern, String text, String replacement) {
+        return pattern.matcher(text).replaceAll(replacement);
+    }
+    
+    private static String regexUpdate(Pattern pattern, String text, Function<MatchResult, String> replacement) {
+        return pattern.matcher(text).replaceAll(replacement);
+    }
+    
+    private static String replaceEscape(String orig) {
+        return orig.replaceAll("\\\\", "\\\\").replaceAll("\\$", "\\$");
+    }
+
     private static void updateProjProperties(File moduleDir, ModuleVersionNumbers thisVersNums) throws IOException {
         File projectPropsFile = moduleDir.toPath().resolve(PROJECT_PROPS_REL_PATH).toFile();
         if (!projectPropsFile.isFile()) {
@@ -204,18 +218,15 @@ private static void updateProjProperties(File moduleDir, ModuleVersionNumbers th
             return;
         }
 
-        OrderedProperties projectProps = new OrderedProperties();
-        try (FileInputStream propsIs = new FileInputStream(projectPropsFile)) {
-            projectProps.load(propsIs);
-        }
-
-        String specVal = projectProps.getProperty(SPEC_PROPS_KEY);
-        if (StringUtils.isNotBlank(specVal)) {
-            projectProps.setProperty(SPEC_PROPS_KEY, thisVersNums.getSpec().getSemVerStr());
+        String projectPropsText = Files.readString(projectPropsFile.toPath());
+        String newText = regexUpdate(PROJECT_PROPS_SPEC_REGEX,
+                projectPropsText,
+                MessageFormat.format(
+                        PROJECT_PROPS_SPEC_REPLACE_FMT,
+                        replaceEscape(thisVersNums.getSpec().getSemVerStr())));
 
-            try (FileOutputStream propsOut = new FileOutputStream(projectPropsFile)) {
-                projectProps.store(propsOut, null);
-            }
+        if (!newText.equals(projectPropsText)) {
+            Files.writeString(projectPropsFile.toPath(), newText);
         }
     }
 
@@ -226,96 +237,79 @@ private static void updateManifest(File moduleDir, ModuleVersionNumbers thisVers
             return;
         }
 
-        Manifest manifest;
-        try (FileInputStream manifestIs = new FileInputStream(manifestFile)) {
-            manifest = ManifestLoader.loadManifest(manifestIs);
-        }
-        Attributes attributes = manifest.getMainAttributes();
-
-        boolean updated = updateAttr(attributes, IMPL_KEY, Integer.toString(thisVersNums.getImplementation()), true);
-        updated = updateAttr(attributes, SPEC_KEY, thisVersNums.getSpec().getSemVerStr(), true) || updated;
-        updated = updateAttr(attributes, RELEASE_KEY, thisVersNums.getRelease().getFullReleaseStr(), true) || updated;
-        if (updated) {
-            try (FileOutputStream manifestOut = new FileOutputStream(manifestFile)) {
-                manifest.write(manifestOut);
-            }
-        }
-    }
-
-    private static boolean updateAttr(Attributes attributes, String key, String val, boolean updateOnlyIfPresent) {
-        if (updateOnlyIfPresent && attributes.getValue(key) == null) {
-            return false;
-        }
-
-        attributes.putValue(key, val);
-        return true;
-    }
+        String manifestFileText = Files.readString(manifestFile.toPath());
+        String newManifestText = regexUpdate(
+                MANIFEST_IMPL_REGEX,
+                manifestFileText,
+                MessageFormat.format(
+                        MANIFEST_IMPL_REPLACE_FMT,
+                        replaceEscape(Integer.toString(thisVersNums.getImplementation()))));
 
-    private static void updateProjXml(File moduleDir, Map<String, ModuleVersionNumbers> versNums)
-            throws IOException, ParserConfigurationException, SAXException, XPathExpressionException, TransformerException {
+        newManifestText = regexUpdate(
+                MANIFEST_SPEC_REGEX,
+                manifestFileText,
+                MessageFormat.format(
+                        MANIFEST_SPEC_REPLACE_FMT,
+                        replaceEscape(thisVersNums.getSpec().getSemVerStr())));
 
-        File projXmlFile = moduleDir.toPath().resolve(PROJ_XML_REL_PATH).toFile();
-        if (!projXmlFile.isFile()) {
-            LOGGER.log(Level.SEVERE, "No project.xml file found at " + projXmlFile.getAbsolutePath());
-            return;
-        }
+        newManifestText = regexUpdate(
+                MANIFEST_RELEASE_REGEX,
+                manifestFileText,
+                MessageFormat.format(
+                        MANIFEST_RELEASE_REPLACE_FMT,
+                        replaceEscape(thisVersNums.getRelease().getFullReleaseStr())));
 
-        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
-        DocumentBuilder db = dbf.newDocumentBuilder();
-        Document projectXmlDoc = db.parse(projXmlFile);
-
-        XPath xPath = XPathFactory.newInstance().newXPath();
-
-        boolean updated = false;
-        for (Entry<String, ModuleVersionNumbers> updatedModule : versNums.entrySet()) {
-            String moduleName = updatedModule.getKey();
-            ModuleVersionNumbers newVers = updatedModule.getValue();
-            Node node = (Node) xPath.compile(MessageFormat.format(PROJ_XML_FMT_STR, moduleName))
-                    .evaluate(projectXmlDoc, XPathConstants.NODE);
-
-            if (node != null) {
-                Map<String, String> childElText = new HashMap<>() {
-                    {
-                        put(PROJ_XML_RELEASE_VERS_EL, newVers.getRelease().getReleaseVersion() == null ? "" : newVers.getRelease().getReleaseVersion().toString());
-                        put(PROJ_XML_IMPL_VERS_EL, Integer.toString(newVers.getImplementation()));
-                        put(PROJ_XML_SPEC_VERS_EL, newVers.getSpec().getSemVerStr());
-                    }
-                };
-                updated = updateXmlChildrenIfPresent(node, childElText) || updated;
-            }
+        if (!newManifestText.equals(manifestFileText)) {
+            Files.writeString(manifestFile.toPath(), newManifestText);
         }
-
-        if (updated) {
-            TransformerFactory transformerFactory = TransformerFactory.newInstance();
-            Transformer transformer = transformerFactory.newTransformer();
-
-            // pretty print XML
-            //transformer.setOutputProperty(OutputKeys.INDENT, "yes");
-            DOMSource source = new DOMSource(projectXmlDoc);
-            try (FileOutputStream xmlOut = new FileOutputStream(projXmlFile)) {
-                StreamResult result = new StreamResult(xmlOut);
-                transformer.transform(source, result);
-            }
-        }
-
     }
 
-    private static boolean updateXmlChildrenIfPresent(Node parentNode, Map<String, String> childElText) {
-        NodeList childNodeList = parentNode.getChildNodes();
-        boolean changed = false;
-        for (int i = 0; i < childNodeList.getLength(); i++) {
-            Node childNode = childNodeList.item(i);
-            String childNodeEl = childNode.getNodeName();
-            String childNodeText = childNode.getTextContent();
-
-            String newChildNodeText = childElText.get(childNodeEl);
-            if (newChildNodeText != null && StringUtils.isNotBlank(childNodeText)) {
-                childNode.setTextContent(newChildNodeText);
-                changed = true;
-            }
-        }
+    private static void updateProjXml(File moduleDir, Map<String, ModuleVersionNumbers> versNums)
+            throws IOException, ParserConfigurationException, SAXException, XPathExpressionException, TransformerException {
 
-        return changed;
+//        File projXmlFile = moduleDir.toPath().resolve(PROJ_XML_REL_PATH).toFile();
+//        if (!projXmlFile.isFile()) {
+//            LOGGER.log(Level.SEVERE, "No project.xml file found at " + projXmlFile.getAbsolutePath());
+//            return;
+//        }
+//
+//        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+//        DocumentBuilder db = dbf.newDocumentBuilder();
+//        Document projectXmlDoc = db.parse(projXmlFile);
+//
+//        XPath xPath = XPathFactory.newInstance().newXPath();
+//
+//        boolean updated = false;
+//        for (Entry<String, ModuleVersionNumbers> updatedModule : versNums.entrySet()) {
+//            String moduleName = updatedModule.getKey();
+//            ModuleVersionNumbers newVers = updatedModule.getValue();
+//            Node node = (Node) xPath.compile(MessageFormat.format(PROJ_XML_FMT_STR, moduleName))
+//                    .evaluate(projectXmlDoc, XPathConstants.NODE);
+//
+//            if (node != null) {
+//                Map<String, String> childElText = new HashMap<>() {
+//                    {
+//                        put(PROJ_XML_RELEASE_VERS_EL, newVers.getRelease().getReleaseVersion() == null ? "" : newVers.getRelease().getReleaseVersion().toString());
+//                        put(PROJ_XML_IMPL_VERS_EL, Integer.toString(newVers.getImplementation()));
+//                        put(PROJ_XML_SPEC_VERS_EL, newVers.getSpec().getSemVerStr());
+//                    }
+//                };
+//                updated = updateXmlChildrenIfPresent(node, childElText) || updated;
+//            }
+//        }
+//
+//        if (updated) {
+//            TransformerFactory transformerFactory = TransformerFactory.newInstance();
+//            Transformer transformer = transformerFactory.newTransformer();
+//
+//            // pretty print XML
+//            //transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+//            DOMSource source = new DOMSource(projectXmlDoc);
+//            try (FileOutputStream xmlOut = new FileOutputStream(projXmlFile)) {
+//                StreamResult result = new StreamResult(xmlOut);
+//                transformer.transform(source, result);
+//            }
+//        }
     }
 
     //        // Spec
-- 
GitLab