javadoc updates

This commit is contained in:
Greg DiCristofaro 2023-09-01 13:34:24 -04:00
parent fb7d735655
commit 8ecbe03749

View File

@ -54,8 +54,11 @@ import org.w3c.dom.NodeList;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
/** /**
* Responsible for gathering current module versions and updating current module
* versions.
* *
* @author gregd * NOTE: During update, this class gives preference to regex replacements as
* opposed to loading and rewriting settings to minimize diffs.
*/ */
public class ModuleUpdates { public class ModuleUpdates {
@ -96,6 +99,15 @@ public class ModuleUpdates {
private static final String XML_HEADER = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"; private static final String XML_HEADER = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
/**
* Parses a SemVer (i.e. 1.23 or 1.23.01) from a string.
*
* @param semVerStr The SemVer string.
* @param defaultSemVer The default SemVer if one cannot be parsed.
* @param resourceForLogging A string identifier of this resource for
* logging purposes.
* @return The parsed SemVer or default.
*/
private static SemVer parseSemVer(String semVerStr, SemVer defaultSemVer, String resourceForLogging) { private static SemVer parseSemVer(String semVerStr, SemVer defaultSemVer, String resourceForLogging) {
if (StringUtils.isBlank(semVerStr)) { if (StringUtils.isBlank(semVerStr)) {
LOGGER.log(Level.SEVERE, MessageFormat.format("Unable to parse semver for empty string in {0}", resourceForLogging)); LOGGER.log(Level.SEVERE, MessageFormat.format("Unable to parse semver for empty string in {0}", resourceForLogging));
@ -109,7 +121,7 @@ public class ModuleUpdates {
int major = StringUtils.isBlank(majorStr) ? 1 : Integer.parseInt(majorStr); int major = StringUtils.isBlank(majorStr) ? 1 : Integer.parseInt(majorStr);
int minor = Integer.parseInt(m.group("minor")); int minor = Integer.parseInt(m.group("minor"));
String patchStr = m.group("patch"); String patchStr = m.group("patch");
Integer patch = StringUtils.isBlank(patchStr) ? null : Integer.parseInt(patchStr); Integer patch = StringUtils.isBlank(patchStr) ? null : Integer.valueOf(patchStr);
return new SemVer(major, minor, patch); return new SemVer(major, minor, patch);
} catch (NullPointerException | NumberFormatException ex) { } catch (NullPointerException | NumberFormatException ex) {
LOGGER.log(Level.SEVERE, MessageFormat.format("Unable to parse semver string {0} for {1}", semVerStr, resourceForLogging), ex); LOGGER.log(Level.SEVERE, MessageFormat.format("Unable to parse semver string {0} for {1}", semVerStr, resourceForLogging), ex);
@ -121,6 +133,14 @@ public class ModuleUpdates {
return defaultSemVer; return defaultSemVer;
} }
/**
* Parses the ReleaseVal object from a release string (i.e. "module/num").
*
* @param releaseStr The release string in format of "module/num".
* @param resourceForLogging A string identifier of this resource for
* logging purposes.
* @return The parsed release val. Release version may be null.
*/
private static ReleaseVal parseReleaseVers(String releaseStr, String resourceForLogging) { private static ReleaseVal parseReleaseVers(String releaseStr, String resourceForLogging) {
Matcher m = RELEASE_REGEX.matcher(StringUtils.defaultString(releaseStr)); Matcher m = RELEASE_REGEX.matcher(StringUtils.defaultString(releaseStr));
if (StringUtils.isNotBlank(releaseStr) && m.find()) { if (StringUtils.isNotBlank(releaseStr) && m.find()) {
@ -128,7 +148,7 @@ public class ModuleUpdates {
Integer releaseNum = null; Integer releaseNum = null;
try { try {
String releaseNumStr = m.group("releaseNum"); String releaseNumStr = m.group("releaseNum");
releaseNum = StringUtils.isBlank(releaseNumStr) ? null : Integer.parseInt(releaseNumStr); releaseNum = StringUtils.isBlank(releaseNumStr) ? null : Integer.valueOf(releaseNumStr);
} catch (NullPointerException | NumberFormatException ex) { } catch (NullPointerException | NumberFormatException ex) {
LOGGER.log(Level.SEVERE, MessageFormat.format("Unable to parse release version string {0} for {1}", releaseStr, resourceForLogging), ex); LOGGER.log(Level.SEVERE, MessageFormat.format("Unable to parse release version string {0} for {1}", releaseStr, resourceForLogging), ex);
} }
@ -141,6 +161,16 @@ public class ModuleUpdates {
} }
/**
* Attempts to parse an integer value from a string returning the default
* value if the string cannot be parsed.
*
* @param str The string.
* @param defaultVal The default value.
* @param resourceForLogging A string identifier of this resource for
* logging purposes.
* @return The parsed value or the default value.
*/
private static int tryParse(String str, int defaultVal, String resourceForLogging) { private static int tryParse(String str, int defaultVal, String resourceForLogging) {
try { try {
return Integer.parseInt(str); return Integer.parseInt(str);
@ -150,6 +180,13 @@ public class ModuleUpdates {
} }
} }
/**
* Parses version numbers from a jar file.
*
* @param jarFile The jar file.
* @return The module version numbers.
* @throws IOException
*/
public static ModuleVersionNumbers getVersionsFromJar(File jarFile) throws IOException { public static ModuleVersionNumbers getVersionsFromJar(File jarFile) throws IOException {
Attributes manifest = ManifestLoader.loadFromJar(jarFile); Attributes manifest = ManifestLoader.loadFromJar(jarFile);
String spec = manifest.getValue(MANIFEST_SPEC_KEY); String spec = manifest.getValue(MANIFEST_SPEC_KEY);
@ -165,48 +202,67 @@ public class ModuleUpdates {
return new ModuleVersionNumbers(jarFile.getName(), specSemVer, implementation, release); return new ModuleVersionNumbers(jarFile.getName(), specSemVer, implementation, release);
} }
static ModuleVersionNumbers getModuleVersionUpdate(ModuleVersionNumbers prev, PublicApiChangeType apiChangeType) { /**
* Calculates new module version numbers based on previous module version
* numbers and the type of API change.
*
* @param prev The previous version numbers.
* @param apiChangeType The public api change type.
* @return The calculated version numbers.
*/
public static ModuleVersionNumbers getModuleVersionUpdate(ModuleVersionNumbers prev, PublicApiChangeType apiChangeType) {
switch (apiChangeType) { switch (apiChangeType) {
case NONE: case NONE -> {
return new ModuleVersionNumbers( return new ModuleVersionNumbers(
prev.getModuleName(), prev.getModuleName(),
prev.getSpec(), prev.getSpec(),
prev.getImplementation() + 1, prev.getImplementation() + 1,
prev.getRelease() prev.getRelease()
); );
case COMPATIBLE_CHANGE: }
case COMPATIBLE_CHANGE -> {
return new ModuleVersionNumbers( return new ModuleVersionNumbers(
prev.getModuleName(), prev.getModuleName(),
new SemVer( new SemVer(
prev.getSpec().getMajor(), prev.getSpec().getMajor(),
prev.getSpec().getMinor() + 1, prev.getSpec().getMinor() + 1,
prev.getSpec().getPatch() prev.getSpec().getPatch()
), ),
prev.getImplementation() + 1, prev.getImplementation() + 1,
new ReleaseVal( new ReleaseVal(
prev.getRelease().getModuleName(), prev.getRelease().getModuleName(),
prev.getRelease().getReleaseVersion() prev.getRelease().getReleaseVersion()
) )
); );
case INCOMPATIBLE_CHANGE: }
case INCOMPATIBLE_CHANGE -> {
return new ModuleVersionNumbers( return new ModuleVersionNumbers(
prev.getModuleName(), prev.getModuleName(),
new SemVer( new SemVer(
prev.getSpec().getMajor() + 1, prev.getSpec().getMajor() + 1,
prev.getSpec().getMinor(), prev.getSpec().getMinor(),
prev.getSpec().getPatch() prev.getSpec().getPatch()
), ),
prev.getImplementation() + 1, prev.getImplementation() + 1,
new ReleaseVal( new ReleaseVal(
prev.getRelease().getModuleName(), prev.getRelease().getModuleName(),
prev.getRelease().getReleaseVersion() == null ? null : prev.getRelease().getReleaseVersion() + 1 prev.getRelease().getReleaseVersion() == null ? null : prev.getRelease().getReleaseVersion() + 1
) )
); );
default: }
default ->
throw new IllegalArgumentException("Unknown api change type: " + apiChangeType); throw new IllegalArgumentException("Unknown api change type: " + apiChangeType);
} }
} }
/**
* Maps release version name (i.e. `org.sleuthkit.autopsy.core`) to a file
* for the directory (i.e. <autopsy repo>/Core).
*
* @param srcDir The autopsy source root directory.
* @return The mapping of release names to directories.
* @throws IOException
*/
private static Map<String, File> getModuleDirs(File srcDir) throws IOException { private static Map<String, File> getModuleDirs(File srcDir) throws IOException {
Map<String, File> moduleDirMapping = new HashMap<>(); Map<String, File> moduleDirMapping = new HashMap<>();
for (File dir : srcDir.listFiles((File f) -> f.isDirectory())) { for (File dir : srcDir.listFiles((File f) -> f.isDirectory())) {
@ -222,6 +278,14 @@ public class ModuleUpdates {
return moduleDirMapping; return moduleDirMapping;
} }
/**
* Updates version numbers in autopsy source. Updates nbm specified versions
* as well as dependencies.
*
* @param srcDir The autopsy source root directory.
* @param versNums The mapping of release name (i.e.
* `org.sleuthkit.autopsy.core` to the new module version numbers).
*/
static void setVersions(File srcDir, Map<String, ModuleVersionNumbers> versNums) { static void setVersions(File srcDir, Map<String, ModuleVersionNumbers> versNums) {
// TODO parse from repo/DIR/manifest.mf release version // TODO parse from repo/DIR/manifest.mf release version
Map<String, File> moduleDirs; Map<String, File> moduleDirs;
@ -238,15 +302,14 @@ public class ModuleUpdates {
LOGGER.log(Level.SEVERE, MessageFormat.format("The following modules were not found in {0}: {1}. Aborting...", srcDir, notFoundModules)); LOGGER.log(Level.SEVERE, MessageFormat.format("The following modules were not found in {0}: {1}. Aborting...", srcDir, notFoundModules));
return; return;
} }
for (Entry<String, File> moduleNameDir : moduleDirs.entrySet()) { for (Entry<String, File> moduleNameDir : moduleDirs.entrySet()) {
String moduleName = moduleNameDir.getKey(); String moduleName = moduleNameDir.getKey();
File moduleDir = moduleNameDir.getValue(); File moduleDir = moduleNameDir.getValue();
ModuleVersionNumbers thisVersNums = versNums.get(moduleName); ModuleVersionNumbers thisVersNums = versNums.get(moduleName);
try { try {
LOGGER.log(Level.INFO, "Updating for module name: " + moduleName); LOGGER.log(Level.INFO, "Updating for module name: {0}", moduleName);
updateProjXml(moduleDir, versNums); updateProjXml(moduleDir, versNums);
if (thisVersNums != null) { if (thisVersNums != null) {
@ -263,14 +326,27 @@ public class ModuleUpdates {
return pattern.matcher(text).replaceAll(replacement); return pattern.matcher(text).replaceAll(replacement);
} }
/**
* Escapes special characters in a regex replace pattern (i.e. '\' and '$').
*
* @param orig The original string value.
* @return The escaped string.
*/
private static String replaceEscape(String orig) { private static String replaceEscape(String orig) {
return orig.replaceAll("\\\\", "\\\\").replaceAll("\\$", "\\$"); return orig.replaceAll("\\\\", "\\\\").replaceAll("\\$", "\\$");
} }
/**
* Updates project.properties version numbers.
*
* @param moduleDir The module directory.
* @param thisVersNums The version numbers to set.
* @throws IOException
*/
private static void updateProjProperties(File moduleDir, ModuleVersionNumbers thisVersNums) throws IOException { private static void updateProjProperties(File moduleDir, ModuleVersionNumbers thisVersNums) throws IOException {
File projectPropsFile = moduleDir.toPath().resolve(PROJECT_PROPS_REL_PATH).toFile(); File projectPropsFile = moduleDir.toPath().resolve(PROJECT_PROPS_REL_PATH).toFile();
if (!projectPropsFile.isFile()) { if (!projectPropsFile.isFile()) {
LOGGER.log(Level.SEVERE, "No project properties found at " + projectPropsFile.getAbsolutePath()); LOGGER.log(Level.SEVERE, "No project properties found at {0}", projectPropsFile.getAbsolutePath());
return; return;
} }
@ -286,10 +362,17 @@ public class ModuleUpdates {
} }
} }
/**
* Updates the manifest.mf file in source to the new module version numbers.
*
* @param moduleDir The module directory.
* @param thisVersNums The new module version numbers.
* @throws IOException
*/
private static void updateManifest(File moduleDir, ModuleVersionNumbers thisVersNums) throws IOException { private static void updateManifest(File moduleDir, ModuleVersionNumbers thisVersNums) throws IOException {
File manifestFile = moduleDir.toPath().resolve(MANIFEST_FILE_NAME).toFile(); File manifestFile = moduleDir.toPath().resolve(MANIFEST_FILE_NAME).toFile();
if (!manifestFile.isFile()) { if (!manifestFile.isFile()) {
LOGGER.log(Level.SEVERE, "No manifest file found at " + manifestFile.getAbsolutePath()); LOGGER.log(Level.SEVERE, "No manifest file found at {0}", manifestFile.getAbsolutePath());
return; return;
} }
@ -303,14 +386,14 @@ public class ModuleUpdates {
newManifestText = regexUpdate( newManifestText = regexUpdate(
MANIFEST_SPEC_REGEX, MANIFEST_SPEC_REGEX,
manifestFileText, newManifestText,
MessageFormat.format( MessageFormat.format(
MANIFEST_SPEC_REPLACE_FMT, MANIFEST_SPEC_REPLACE_FMT,
replaceEscape(thisVersNums.getSpec().getSemVerStr()))); replaceEscape(thisVersNums.getSpec().getSemVerStr())));
newManifestText = regexUpdate( newManifestText = regexUpdate(
MANIFEST_RELEASE_REGEX, MANIFEST_RELEASE_REGEX,
manifestFileText, newManifestText,
MessageFormat.format( MessageFormat.format(
MANIFEST_RELEASE_REPLACE_FMT, MANIFEST_RELEASE_REPLACE_FMT,
replaceEscape(thisVersNums.getRelease().getFullReleaseStr()))); replaceEscape(thisVersNums.getRelease().getFullReleaseStr())));
@ -320,12 +403,24 @@ public class ModuleUpdates {
} }
} }
/**
* Updates project.xml to new version numbers. This method uses xml
* parsing/writing instead of regex replacements to avoid xml errors.
*
* @param moduleDir The module directory.
* @param versNums The new version numbers.
* @throws IOException
* @throws ParserConfigurationException
* @throws SAXException
* @throws XPathExpressionException
* @throws TransformerException
*/
private static void updateProjXml(File moduleDir, Map<String, ModuleVersionNumbers> versNums) private static void updateProjXml(File moduleDir, Map<String, ModuleVersionNumbers> versNums)
throws IOException, ParserConfigurationException, SAXException, XPathExpressionException, TransformerException { throws IOException, ParserConfigurationException, SAXException, XPathExpressionException, TransformerException {
File projXmlFile = moduleDir.toPath().resolve(PROJ_XML_REL_PATH).toFile(); File projXmlFile = moduleDir.toPath().resolve(PROJ_XML_REL_PATH).toFile();
if (!projXmlFile.isFile()) { if (!projXmlFile.isFile()) {
LOGGER.log(Level.SEVERE, "No project.xml file found at " + projXmlFile.getAbsolutePath()); LOGGER.log(Level.SEVERE, "No project.xml file found at {0}", projXmlFile.getAbsolutePath());
return; return;
} }
@ -368,6 +463,14 @@ public class ModuleUpdates {
} }
} }
/**
* Update xml children if present.
*
* @param parentNode The parent node.
* @param childElText The mapping of child element names to new text
* content.
* @return True if a change occurred.
*/
private static boolean updateXmlChildrenIfPresent(Node parentNode, Map<String, String> childElText) { private static boolean updateXmlChildrenIfPresent(Node parentNode, Map<String, String> childElText) {
NodeList childNodeList = parentNode.getChildNodes(); NodeList childNodeList = parentNode.getChildNodes();
boolean changed = false; boolean changed = false;
@ -386,23 +489,10 @@ public class ModuleUpdates {
return changed; return changed;
} }
// // Spec /**
// // project.properties * The release value originally in format like
// // spec.version.base * `org.sleuthkit.autopsy.core/5`.
// // manifest */
// // OpenIDE-Module-Specification-Version
// // Implementation
// // manifest
// // OpenIDE-Module-Implementation-Version
// // Release
// // manifest
// // OpenIDE-Module (/number)
//
// // Dependency specification
// // project.xml
// // project.configuration.data.module-dependencies.dependency.run-dependency:
// // specification-version
// // release-version
public static class ReleaseVal { public static class ReleaseVal {
private final String moduleName; private final String moduleName;
@ -413,14 +503,23 @@ public class ModuleUpdates {
this.releaseVersion = releaseVersion; this.releaseVersion = releaseVersion;
} }
/**
* @return The module name (i.e. `org.sleuthkit.autopsy.core`).
*/
public String getModuleName() { public String getModuleName() {
return moduleName; return moduleName;
} }
/**
* @return The release version if one specified else null.
*/
public Integer getReleaseVersion() { public Integer getReleaseVersion() {
return releaseVersion; return releaseVersion;
} }
/**
* @return The full release string like `org.sleuthkit.autopsy.core/5`.
*/
public String getFullReleaseStr() { public String getFullReleaseStr() {
return this.releaseVersion == null return this.releaseVersion == null
? moduleName ? moduleName
@ -428,6 +527,9 @@ public class ModuleUpdates {
} }
} }
/**
* SemVer object (i.e. 1.3.5).
*/
public static class SemVer { public static class SemVer {
private final int major; private final int major;
@ -440,18 +542,30 @@ public class ModuleUpdates {
this.patch = patch; this.patch = patch;
} }
/**
* @return Major version.
*/
public int getMajor() { public int getMajor() {
return major; return major;
} }
/**
* @return Minor version.
*/
public int getMinor() { public int getMinor() {
return minor; return minor;
} }
/**
* @return Patch version (can be null).
*/
public Integer getPatch() { public Integer getPatch() {
return patch; return patch;
} }
/**
* @return The SemVer string (i.e. `1.5` or `1.5.3`).
*/
public String getSemVerStr() { public String getSemVerStr() {
return (patch == null) return (patch == null)
? MessageFormat.format("{0,number,#}.{1,number,#}", major, minor) ? MessageFormat.format("{0,number,#}.{1,number,#}", major, minor)
@ -459,6 +573,9 @@ public class ModuleUpdates {
} }
} }
/**
* Module version numbers record.
*/
public static class ModuleVersionNumbers { public static class ModuleVersionNumbers {
private final String moduleName; private final String moduleName;
@ -473,18 +590,30 @@ public class ModuleUpdates {
this.release = release; this.release = release;
} }
/**
* @return The name of the module (i.e. org-sleuthkit-autopsy-core)
*/
public String getModuleName() { public String getModuleName() {
return moduleName; return moduleName;
} }
/**
* @return The specification SemVer.
*/
public SemVer getSpec() { public SemVer getSpec() {
return spec; return spec;
} }
/**
* @return The implementation number.
*/
public int getImplementation() { public int getImplementation() {
return implementation; return implementation;
} }
/**
* @return The release name/version.
*/
public ReleaseVal getRelease() { public ReleaseVal getRelease() {
return release; return release;
} }