javadoc updates

This commit is contained in:
Greg DiCristofaro 2023-09-01 13:07:43 -04:00
parent da37197a03
commit fb7d735655
6 changed files with 210 additions and 55 deletions

View File

@ -55,14 +55,22 @@ import javassist.CtMember;
import javassist.Modifier;
/**
*
* @author gregd
* Handles diffing the public API between two jar files.
*/
public class APIDiff {
// filters to a jar or nbm file
private static final FileFilter JAR_FILTER
= (File f) -> f.isFile() && (f.getName().toLowerCase().endsWith(".jar") || f.getName().toLowerCase().endsWith(".nbm"));
/**
* Identifies common jar files between two directories. Only files listed in
* the directory are considered. This method does not recurse.
*
* @param prevDir The previous version directory.
* @param currDir The current version directory.
* @return The jar file names.
*/
static List<String> getCommonJars(File prevDir, File currDir) {
Set<String> prevJars = getJars(prevDir);
Set<String> currJars = getJars(currDir);
@ -74,12 +82,27 @@ public class APIDiff {
return commonJars.stream().sorted().collect(Collectors.toList());
}
/**
* Returns all jar files listed in directory (does not recurse).
*
* @param dir The directory.
* @return The jar file names.
*/
private static Set<String> getJars(File dir) {
return Stream.of(dir.listFiles(JAR_FILTER))
.map(f -> f.getName())
.collect(Collectors.toSet());
}
/**
* Uses manfest.mf specification of "OpenIDE-Module-Public-Packages" to
* determine public API packages.
*
* @param jarFile The jar file.
* @return The set of package names.
* @throws IOException
* @throws IllegalStateException
*/
private static Set<String> getPublicPackages(File jarFile) throws IOException, IllegalStateException {
String publicPackageStr = ManifestLoader.loadFromJar(jarFile).getValue("OpenIDE-Module-Public-Packages");
if (publicPackageStr == null) {
@ -92,11 +115,26 @@ public class APIDiff {
}
}
// only fields, methods that are public or protected
/**
* Filter to identify non-public, non-protected members for exclusion.
*
* @param member The CtMember (field/method).
* @return True if should be excluded (private/package private).
*/
static boolean excludeMember(CtMember member) {
return !Modifier.isPublic(member.getModifiers()) && !Modifier.isProtected(member.getModifiers());
}
/**
* Compares two jar files.
*
* @param prevVersion The name of the previous version.
* @param curVersion The name of the current version.
* @param prevJar The previous version jar file.
* @param curJar The current version jar file.
* @return A record describing the comparison in public API.
* @throws IOException
*/
static ComparisonRecord getComparison(String prevVersion, String curVersion, File prevJar, File curJar) throws IOException {
// scope only to previous or current public packages
Set<String> prevPublicApiPackages = getPublicPackages(prevJar);
@ -150,6 +188,13 @@ public class APIDiff {
return new ComparisonRecord(prevVersion, curVersion, prevJar, curJar, humanReadableApiChange, changeType, onlyPrevApiPackages, onlyCurApiPackages, commonApiPackages);
}
/**
* Updates an atomic ref to the public api change type to the maximum change
* (where no change is min and incompatible change is max).
*
* @param apiChangeRef The atomic ref to a public api change type.
* @param tp The possibly new change type.
*/
private static void updateToMax(AtomicReference<PublicApiChangeType> apiChangeRef, JApiHasChangeStatus tp) {
PublicApiChangeType apiChangeType;
switch (tp.getChangeStatus()) {
@ -164,14 +209,20 @@ public class APIDiff {
default:
apiChangeType = PublicApiChangeType.INCOMPATIBLE_CHANGE;
break;
};
}
final PublicApiChangeType finalApiChangeType = apiChangeType;
apiChangeRef.updateAndGet((refType) -> Comparators.max(refType, finalApiChangeType));
}
static PublicApiChangeType getChangeType(List<JApiClass> jApiClasses) {
AtomicReference<PublicApiChangeType> apiChange = new AtomicReference<PublicApiChangeType>(PublicApiChangeType.NONE);
/**
* Determines the public api change type for the given classes.
*
* @param jApiClasses The classes.
* @return The public API change type.
*/
static PublicApiChangeType getChangeType(List<JApiClass> jApiClasses) {
AtomicReference<PublicApiChangeType> apiChange = new AtomicReference<>(PublicApiChangeType.NONE);
Filter.filter(jApiClasses, new Filter.FilterVisitor() {
@Override
@ -213,6 +264,10 @@ public class APIDiff {
return apiChange.get();
}
/**
* A record describing the public API comparison of a previous and current
* version.
*/
public static class ComparisonRecord {
private final String prevVersion;
@ -237,43 +292,70 @@ public class APIDiff {
this.commonApiPackages = commonApiPackages;
}
/**
* @return The previous version name.
*/
public String getPrevVersion() {
return prevVersion;
}
/**
* @return The current version name.
*/
public String getCurVersion() {
return curVersion;
}
/**
* @return The previous version jar file.
*/
public File getPrevJar() {
return prevJar;
}
/**
* @return The current version jar file.
*/
public File getCurJar() {
return curJar;
}
/**
* @return The human readable output describing the api changes.
*/
public String getHumanReadableApiChange() {
return humanReadableApiChange;
}
/**
* @return The public api change type.
*/
public PublicApiChangeType getChangeType() {
return changeType;
}
/**
* @return Names of packages only in previous public API.
*/
public Set<String> getOnlyPrevApiPackages() {
return onlyPrevApiPackages;
}
/**
* @return Names of packages only in current public API.
*/
public Set<String> getOnlyCurrApiPackages() {
return onlyCurrApiPackages;
}
/**
* @return Names of packages in common between previous and current
* public API.
*/
public Set<String> getCommonApiPackages() {
return commonApiPackages;
}
}
}

View File

@ -32,12 +32,11 @@ import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
/**
*
* @author gregd
* Processes CLI options.
*/
public class CLIProcessor {
static Option PREV_VERS_PATH_OPT = Option.builder()
private static final Option PREV_VERS_PATH_OPT = Option.builder()
.argName("path")
.desc("The path to the previous version jar files")
.hasArg(true)
@ -46,7 +45,7 @@ public class CLIProcessor {
.required(true)
.build();
static Option CUR_VERS_PATH_OPT = Option.builder()
private static final Option CUR_VERS_PATH_OPT = Option.builder()
.argName("path")
.desc("The path to the current version jar files")
.hasArg(true)
@ -55,7 +54,7 @@ public class CLIProcessor {
.required(false)
.build();
static Option PREV_VERS_OPT = Option.builder()
private static final Option PREV_VERS_OPT = Option.builder()
.argName("version")
.desc("The previous version number")
.hasArg(true)
@ -64,7 +63,7 @@ public class CLIProcessor {
.required(false)
.build();
static Option CUR_VERS_OPT = Option.builder()
private static final Option CUR_VERS_OPT = Option.builder()
.argName("version")
.desc("The current version number")
.hasArg(true)
@ -73,7 +72,7 @@ public class CLIProcessor {
.required(false)
.build();
static Option SRC_LOC_OPT = Option.builder()
private static final Option SRC_LOC_OPT = Option.builder()
.argName("path")
.desc("The path to the root of the autopsy repor")
.hasArg(true)
@ -82,7 +81,7 @@ public class CLIProcessor {
.required(true)
.build();
static Option UPDATE_OPT = Option.builder()
private static final Option UPDATE_OPT = Option.builder()
.desc("Update source code versions")
.hasArg(false)
.longOpt("update")
@ -90,7 +89,7 @@ public class CLIProcessor {
.required(false)
.build();
static List<Option> ALL_OPTIONS = Arrays.asList(
private static final List<Option> ALL_OPTIONS = Arrays.asList(
PREV_VERS_PATH_OPT,
CUR_VERS_PATH_OPT,
PREV_VERS_OPT,
@ -99,11 +98,16 @@ public class CLIProcessor {
UPDATE_OPT
);
static Options CLI_OPTIONS = getCliOptions(ALL_OPTIONS);
private static final Options CLI_OPTIONS = getCliOptions(ALL_OPTIONS);
private static final String DEFAULT_CURR_VERSION = "Current Version";
private static final String DEFAULT_PREV_VERSION = "Previous Version";
private static final String BUILD_REL_PATH = "build/cluster/modules";
/**
* Creates an Options object from a list of options.
* @param opts The list of options.
* @return The options object.
*/
private static Options getCliOptions(List<Option> opts) {
Options toRet = new Options();
for (Option opt : opts) {
@ -113,7 +117,7 @@ public class CLIProcessor {
return toRet;
}
static Option HELP_OPT = Option.builder()
private static final Option HELP_OPT = Option.builder()
.desc("Print help message")
.hasArg(false)
.longOpt("help")
@ -121,12 +125,16 @@ public class CLIProcessor {
.required(false)
.build();
static Options HELP_OPTIONS = getCliOptions(Collections.singletonList(HELP_OPT));
private static final Options HELP_OPTIONS = getCliOptions(Collections.singletonList(HELP_OPT));
private static CommandLineParser parser = new DefaultParser();
private static final CommandLineParser parser = new DefaultParser();
private static HelpFormatter helpFormatter = new HelpFormatter();
private static final HelpFormatter helpFormatter = new HelpFormatter();
/**
* Prints help message.
* @param ex The exception or null if no exception.
*/
static void printHelp(Exception ex) {
if (ex != null && ex.getMessage() != null && !ex.getMessage().isBlank()) {
System.out.println(ex.getMessage());
@ -135,6 +143,12 @@ public class CLIProcessor {
helpFormatter.printHelp("APIUpdate", CLI_OPTIONS);
}
/**
* Parses the CLI args.
* @param args The arguments.
* @return The CLIArgs object.
* @throws ParseException
*/
static CLIArgs parseCli(String[] args) throws ParseException {
CommandLine helpCmd = parser.parse(HELP_OPTIONS, args, true);
boolean isHelp = helpCmd.hasOption(HELP_OPT);
@ -171,6 +185,9 @@ public class CLIProcessor {
return new CLIArgs(curVers, prevVers, curVersFile, prevVersFile, srcPathFile, makeUpdate, false);
}
/**
* The CLI args object.
*/
public static class CLIArgs {
private final String currentVersion;
@ -191,30 +208,52 @@ public class CLIProcessor {
this.makeUpdate = makeUpdate;
}
/**
* @return The current version name.
*/
public String getCurrentVersion() {
return currentVersion;
}
/**
* @return The previous version name.
*/
public String getPreviousVersion() {
return previousVersion;
}
/**
* @return The path to the directory containing the jars for current version.
*/
public File getCurrentVersPath() {
return currentVersPath;
}
/**
* @return The path to the directory containing the jars for previous version.
*/
public File getPreviousVersPath() {
return previousVersPath;
}
public boolean isIsHelp() {
/**
* @return True if only print help message.
*/
public boolean isHelp() {
return isHelp;
}
/**
* @return True if module versions should be updated.
*/
public boolean isMakeUpdate() {
return makeUpdate;
}
/**
* @return The path to the source directory root for autopsy.
*/
public File getSrcPath() {
return srcPath;
}

View File

@ -30,19 +30,17 @@ import org.sleuthkit.autopsy.apiupdate.CLIProcessor.CLIArgs;
import org.sleuthkit.autopsy.apiupdate.ModuleUpdates.ModuleVersionNumbers;
/**
*
* @author gregd
* Main class.
*/
public class Main {
private static final Logger LOGGER = Logger.getLogger(Main.class.getName());
public static void main(String[] args) {
args = "-p C:\\Users\\gregd\\Desktop\\apidiff\\old -s C:\\Users\\gregd\\Documents\\Source\\autopsy".split(" ");
CLIArgs cliArgs;
try {
cliArgs = CLIProcessor.parseCli(args);
if (cliArgs.isIsHelp()) {
if (cliArgs.isHelp()) {
CLIProcessor.printHelp(null);
System.exit(0);
}
@ -81,28 +79,40 @@ public class Main {
}
/**
* Outputs the difference between previous and current version public API.
*
* @param commonJarFileName The common jar file name.
* @param record The comparison record.
* @param prevVersionNums The version numbers in previous version.
* @param projectedVersionNums The calculated version numbers for current
* version.
*/
private static void outputDiff(
String commonJarFileName,
ComparisonRecord record,
ModuleVersionNumbers prevVersionNums,
ModuleVersionNumbers projectedVersionNums
) {
LOGGER.log(Level.INFO, MessageFormat.format("\n"
+ "====================================\n"
+ "DIFF FOR: {0}\n"
+ "Public API Change Type: {1}\n"
+ "Previous Version Numbers:\n"
+ " - release: {2}\n"
+ " - specification: {3}\n"
+ " - implementation: {4}\n"
+ "Current Version Numbers:\n"
+ " - release: {5}\n"
+ " - specification: {6}\n"
+ " - implementation: {7}\n"
+ "====================================\n"
+ "Public API packages only in previous: {8}\n"
+ "Public API packages only in current: {9}\n"
+ "{10}\n\n",
LOGGER.log(Level.INFO, MessageFormat.format("""
====================================
DIFF FOR: {0}
Public API Change Type: {1}
Previous Version Numbers:
- release: {2}
- specification: {3}
- implementation: {4}
Current Version Numbers:
- release: {5}
- specification: {6}
- implementation: {7}
====================================
Public API packages only in previous: {8}
Public API packages only in current: {9}
{10}
""",
commonJarFileName,
record.getChangeType(),
prevVersionNums.getRelease().getFullReleaseStr(),

View File

@ -29,22 +29,39 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
*
* @author gregd
* Loads manifest.mf files.
*/
public class ManifestLoader {
private static final String JAR_MANIFEST_REL_PATH = "META-INF/MANIFEST.MF";
public static Attributes loadInputStream(InputStream is) throws IOException {
/**
* Loads the manifest attributes from an input stream.
* @param is The input stream.
* @return The manifest attributes.
* @throws IOException
*/
public static Attributes loadManifestAttributes(InputStream is) throws IOException {
Manifest manifest = loadManifest(is);
return manifest.getMainAttributes();
}
/**
* Loads the manifest from an input stream.
* @param is The input stream.
* @return The manifest.
* @throws IOException
*/
public static Manifest loadManifest(InputStream is) throws IOException {
return new Manifest(is);
}
/**
* Loads manifest attributes from a jar file.
* @param jarFile The jar file.
* @return The manifest attributes.
* @throws IOException
*/
public static Attributes loadFromJar(File jarFile) throws IOException {
ZipFile zipFile = new ZipFile(jarFile);
@ -53,7 +70,7 @@ public class ManifestLoader {
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
if (JAR_MANIFEST_REL_PATH.equalsIgnoreCase(entry.getName())) {
return loadInputStream(zipFile.getInputStream(entry));
return loadManifestAttributes(zipFile.getInputStream(entry));
}
}

View File

@ -32,8 +32,6 @@ import java.util.Set;
import java.util.jar.Attributes;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import java.util.logging.StreamHandler;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.parsers.DocumentBuilder;
@ -215,7 +213,7 @@ public class ModuleUpdates {
File manifestFile = dir.toPath().resolve(MANIFEST_FILE_NAME).toFile();
if (manifestFile.isFile()) {
try (FileInputStream manifestIs = new FileInputStream(manifestFile)) {
Attributes manifestAttrs = ManifestLoader.loadInputStream(manifestIs);
Attributes manifestAttrs = ManifestLoader.loadManifestAttributes(manifestIs);
ReleaseVal releaseVal = parseReleaseVers(manifestAttrs.getValue(MANIFEST_RELEASE_KEY), manifestFile.getAbsolutePath());
moduleDirMapping.put(releaseVal.getModuleName(), dir);
}

View File

@ -22,18 +22,27 @@ import java.util.Comparator;
import org.apache.commons.lang3.ObjectUtils;
/**
*
* @author gregd
* A public API change type (no change, compatible change, incompatible change).
*/
public enum PublicApiChangeType implements Comparator<PublicApiChangeType> {
NONE(0), COMPATIBLE_CHANGE(1), INCOMPATIBLE_CHANGE(2);
private int level;
/**
* COnstructor.
*
* @param level The level for the api change (none is min, incompatible is
* max).
*/
PublicApiChangeType(int level) {
this.level = level;
}
/**
*
* @return The level for the api change (none is min, incompatible is max).
*/
public int getLevel() {
return level;
}