This commit is contained in:
Greg DiCristofaro 2023-09-01 12:24:21 -04:00
parent 10d27316a6
commit 0b7e8dd8ca
7 changed files with 341 additions and 965 deletions

View File

@ -4,20 +4,40 @@
*/
package org.sleuthkit.autopsy.apiupdate;
import com.google.common.collect.Comparators;
import japicmp.cmp.JApiCmpArchive;
import japicmp.cmp.JarArchiveComparator;
import japicmp.cmp.JarArchiveComparatorOptions;
import japicmp.config.Options;
import japicmp.filter.BehaviorFilter;
import japicmp.filter.ClassFilter;
import japicmp.filter.FieldFilter;
import japicmp.model.JApiAnnotation;
import japicmp.model.JApiClass;
import japicmp.model.JApiConstructor;
import japicmp.model.JApiField;
import japicmp.model.JApiHasChangeStatus;
import japicmp.model.JApiImplementedInterface;
import japicmp.model.JApiMethod;
import japicmp.model.JApiSuperclass;
import japicmp.output.Filter;
import japicmp.output.stdout.StdoutOutputGenerator;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javassist.CtBehavior;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMember;
import javassist.Modifier;
/**
*
@ -57,107 +77,186 @@ public class APIDiff {
}
}
static void getComparison(String prevVersion, String curVersion, File prevJar, File curJar) throws IOException {
// only fields, methods that are public or protected
static boolean excludeMember(CtMember member) {
return !Modifier.isPublic(member.getModifiers()) && !Modifier.isProtected(member.getModifiers());
}
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);
Set<String> curPublicApiPackages = getPublicPackages(curJar);
Set<String> allPublicApiPackages = new HashSet<>();
allPublicApiPackages.addAll(prevPublicApiPackages);
allPublicApiPackages.addAll(curPublicApiPackages);
Set<String> onlyPrevApiPackages = new HashSet<>();
Set<String> onlyCurApiPackages = new HashSet<>();
Set<String> commonApiPackages = new HashSet<>();
for (String apiPackage : allPublicApiPackages) {
boolean inPrev = prevPublicApiPackages.contains(apiPackage);
boolean inCur = curPublicApiPackages.contains(apiPackage);
if (inPrev && !inCur) {
onlyPrevApiPackages.add(apiPackage);
} else if (!inPrev && inCur) {
onlyCurApiPackages.add(apiPackage);
} else {
commonApiPackages.add(apiPackage);
}
}
JarArchiveComparatorOptions comparatorOptions = new JarArchiveComparatorOptions();
//comparatorOptions.setAccessModifier(AccessModifier.);
// only classes in prev or current public api
comparatorOptions.getFilters().getExcludes().add((ClassFilter) (CtClass ctClass) -> !allPublicApiPackages.contains(ctClass.getPackageName()));
// only public classes
comparatorOptions.getFilters().getExcludes().add((ClassFilter) (CtClass ctClass) -> !Modifier.isPublic(ctClass.getModifiers()));
// only fields, methods that are public or protected and class is not final
comparatorOptions.getFilters().getExcludes().add((FieldFilter) (CtField ctField) -> excludeMember(ctField));
comparatorOptions.getFilters().getExcludes().add((BehaviorFilter) (CtBehavior ctBehavior) -> excludeMember(ctBehavior));
comparatorOptions.getIgnoreMissingClasses().setIgnoreAllMissingClasses(true);
JarArchiveComparator jarArchiveComparator = new JarArchiveComparator(comparatorOptions);
List<JApiClass> jApiClasses = jarArchiveComparator.compare(
new JApiCmpArchive(prevJar, prevVersion),
new JApiCmpArchive(curJar, curVersion)
);
Set<String> prevPublicApiPackages = getPublicPackages(prevJar);
Set<String> curPublicApiPackages = getPublicPackages(curJar);
// TODO handle diff in this list
Set<String> allPublicApiPackages = new HashSet<>();
allPublicApiPackages.addAll(prevPublicApiPackages);
allPublicApiPackages.addAll(curPublicApiPackages);
jApiClasses = jApiClasses.stream()
.filter(cls -> allPublicApiPackages.contains(cls.getNewClass().or(cls.getOldClass()).get().getPackageName()))
.collect(Collectors.toList());
PublicApiChangeType changeType = getChangeType(jApiClasses);
Options options = Options.newDefault();
options.setOutputOnlyModifications(true);
System.out.println("Comparing " + prevJar.getName());
ChangeOutputGenerator stdoutOutputGenerator = new ChangeOutputGenerator(options, jApiClasses);
String output = stdoutOutputGenerator.generate();
System.out.println(output);
StdoutOutputGenerator stdoutOutputGenerator = new StdoutOutputGenerator(options, jApiClasses);
String humanReadableApiChange = stdoutOutputGenerator.generate();
return new ComparisonRecord(prevVersion, curVersion, prevJar, curJar, humanReadableApiChange, changeType, onlyPrevApiPackages, onlyCurApiPackages, commonApiPackages);
}
private static void generateOutput(Options options, List<JApiClass> jApiClasses, JarArchiveComparator jarArchiveComparator) {
// for (JApiClass cls: jApiClasses) {
// cls.is
// }
//
private static void updateToMax(AtomicReference<PublicApiChangeType> apiChangeRef, JApiHasChangeStatus tp) {
PublicApiChangeType apiChangeType;
switch (tp.getChangeStatus()) {
case UNCHANGED:
apiChangeType = PublicApiChangeType.NONE;
break;
case NEW:
apiChangeType = PublicApiChangeType.COMPATIBLE_CHANGE;
break;
case MODIFIED:
case REMOVED:
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);
Filter.filter(jApiClasses, new Filter.FilterVisitor() {
@Override
public void visit(Iterator<JApiClass> itrtr, JApiClass jac) {
updateToMax(apiChange, jac);
}
@Override
public void visit(Iterator<JApiMethod> itrtr, JApiMethod jam) {
updateToMax(apiChange, jam);
}
@Override
public void visit(Iterator<JApiConstructor> itrtr, JApiConstructor jac) {
updateToMax(apiChange, jac);
}
@Override
public void visit(Iterator<JApiImplementedInterface> itrtr, JApiImplementedInterface jaii) {
updateToMax(apiChange, jaii);
}
@Override
public void visit(Iterator<JApiField> itrtr, JApiField jaf) {
updateToMax(apiChange, jaf);
}
@Override
public void visit(Iterator<JApiAnnotation> itrtr, JApiAnnotation jaa) {
updateToMax(apiChange, jaa);
}
@Override
public void visit(JApiSuperclass jas) {
updateToMax(apiChange, jas);
}
});
return apiChange.get();
}
public static class ComparisonRecord {
private final String prevVersion;
private final String curVersion;
private final File prevJar;
private final File curJar;
private final String humanReadableApiChange;
private final PublicApiChangeType changeType;
private final Set<String> onlyPrevApiPackages;
private final Set<String> onlyCurrApiPackages;
private final Set<String> commonApiPackages;
public ComparisonRecord(String prevVersion, String curVersion, File prevJar, File curJar, String humanReadableApiChange, PublicApiChangeType changeType, Set<String> onlyPrevApiPackages, Set<String> onlyCurrApiPackages, Set<String> commonApiPackages) {
this.prevVersion = prevVersion;
this.curVersion = curVersion;
this.prevJar = prevJar;
this.curJar = curJar;
this.humanReadableApiChange = humanReadableApiChange;
this.changeType = changeType;
this.onlyPrevApiPackages = onlyPrevApiPackages;
this.onlyCurrApiPackages = onlyCurrApiPackages;
this.commonApiPackages = commonApiPackages;
}
public String getPrevVersion() {
return prevVersion;
}
public String getCurVersion() {
return curVersion;
}
public File getPrevJar() {
return prevJar;
}
public File getCurJar() {
return curJar;
}
public String getHumanReadableApiChange() {
return humanReadableApiChange;
}
public PublicApiChangeType getChangeType() {
return changeType;
}
public Set<String> getOnlyPrevApiPackages() {
return onlyPrevApiPackages;
}
public Set<String> getOnlyCurrApiPackages() {
return onlyCurrApiPackages;
}
public Set<String> getCommonApiPackages() {
return commonApiPackages;
}
// if (options.isSemanticVersioning()) {
// SemverOut semverOut = new SemverOut(options, jApiClasses);
// String output = semverOut.generate();
// System.out.println(output);
// return;
// }
// if (options.getXmlOutputFile().isPresent() || options.getHtmlOutputFile().isPresent()) {
// SemverOut semverOut = new SemverOut(options, jApiClasses);
// XmlOutputGeneratorOptions xmlOutputGeneratorOptions = new XmlOutputGeneratorOptions();
// xmlOutputGeneratorOptions.setCreateSchemaFile(true);
// xmlOutputGeneratorOptions.setSemanticVersioningInformation(semverOut.generate());
// XmlOutputGenerator xmlGenerator = new XmlOutputGenerator(jApiClasses, options, xmlOutputGeneratorOptions);
// try (XmlOutput xmlOutput = xmlGenerator.generate()) {
// XmlOutputGenerator.writeToFiles(options, xmlOutput);
// } catch (Exception e) {
// throw new JApiCmpException(JApiCmpException.Reason.IoException, "Could not close output streams: " + e.getMessage(), e);
// }
// }
// StdoutOutputGenerator stdoutOutputGenerator = new StdoutOutputGenerator(options, jApiClasses);
// String output = stdoutOutputGenerator.generate();
// System.out.println(output);
// if (options.isErrorOnBinaryIncompatibility()
// || options.isErrorOnSourceIncompatibility()
// || options.isErrorOnExclusionIncompatibility()
// || options.isErrorOnModifications()
// || options.isErrorOnSemanticIncompatibility()) {
// IncompatibleErrorOutput errorOutput = new IncompatibleErrorOutput(options, jApiClasses, jarArchiveComparator);
// errorOutput.generate();
// }
}
// enum ChangeType { CHANGE, ADD, REMOVE }
//
// public class ClassChangeDTO {
// private final String packageStr;
// private final String fullyQualifiedClassName;
// private final ChangeType changeType;
//
// private final ClassDataDTO prevClassRecord;
// private final ClassDataDTO currClassRecord;
//
//
// }
//
// public class ClassDataDTO {
// private final String packageStr;
// private final String fullyQualifiedClassName;
// private final AccessModifier accessModifier;
// }
//
}

View File

@ -1,153 +0,0 @@
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package org.sleuthkit.autopsy.apiupdate;
import japicmp.model.JApiChangeStatus;
import java.util.Optional;
/**
*
* @author gregd
*/
public class ApiChangeDTO {
//NEW, REMOVED, UNCHANGED, MODIFIED
public interface PublicApiChangeable {
PublicApiChangeType getChangeType();
}
public static class ClassChangeDTO {
private final JApiChangeStatus changeStatus;
private final Optional<String> oldDeclaration;
private final Optional<Long> oldSerialId;
private final Optional<String> newDeclaration;
private final Optional<Long> newSerialId;
public ClassChangeDTO(JApiChangeStatus changeStatus, Optional<String> oldDeclaration, Optional<Long> oldSerialId, Optional<String> newDeclaration, Optional<Long> newSerialId) {
this.changeStatus = changeStatus;
this.oldDeclaration = oldDeclaration;
this.oldSerialId = oldSerialId;
this.newDeclaration = newDeclaration;
this.newSerialId = newSerialId;
}
public JApiChangeStatus getChangeStatus() {
return changeStatus;
}
public Optional<String> getOldDeclaration() {
return oldDeclaration;
}
public Optional<Long> getOldSerialId() {
return oldSerialId;
}
public Optional<String> getNewDeclaration() {
return newDeclaration;
}
public Optional<Long> getNewSerialId() {
return newSerialId;
}
}
// public static class SuperclassChangeDTO {
//
// private final Optional<String> oldFullyQualifiedClassName;
// private final Optional<String> newFullyQualifiedClassName;
//
// public SuperclassChangeDTO(Optional<String> oldFullyQualifiedClassName, Optional<String> newFullyQualifiedClassName) {
// this.oldFullyQualifiedClassName = oldFullyQualifiedClassName;
// this.newFullyQualifiedClassName = newFullyQualifiedClassName;
// }
//
// public Optional<String> getOldFullyQualifiedClassName() {
// return oldFullyQualifiedClassName;
// }
//
// public Optional<String> getNewFullyQualifiedClassName() {
// return newFullyQualifiedClassName;
// }
//
// }
//
// public static class InterfaceChangeDTO {
//
// private final JApiChangeStatus changeStatus;
// private final String fullyQualifiedName;
//
// public InterfaceChangeDTO(JApiChangeStatus changeStatus, String fullyQualifiedName) {
// this.changeStatus = changeStatus;
// this.fullyQualifiedName = fullyQualifiedName;
// }
//
// public JApiChangeStatus getChangeStatus() {
// return changeStatus;
// }
//
// public String getFullyQualifiedName() {
// return fullyQualifiedName;
// }
//
// }
public static class MethodChangeDTO {
private final JApiChangeStatus changeStatus;
private final Optional<String> oldMethodDeclaration;
private final Optional<String> newMethodDeclaration;
public MethodChangeDTO(JApiChangeStatus changeStatus, Optional<String> oldMethodDeclaration, Optional<String> newMethodDeclaration) {
this.changeStatus = changeStatus;
this.oldMethodDeclaration = oldMethodDeclaration;
this.newMethodDeclaration = newMethodDeclaration;
}
public JApiChangeStatus getChangeStatus() {
return changeStatus;
}
public Optional<String> getOldMethodDeclaration() {
return oldMethodDeclaration;
}
public Optional<String> getNewMethodDeclaration() {
return newMethodDeclaration;
}
}
public static class FieldChangeDTO {
private final JApiChangeStatus changeStatus;
private final Optional<String> oldFieldDeclaration;
private final Optional<String> newFieldDeclaration;
public FieldChangeDTO(JApiChangeStatus changeStatus, Optional<String> oldFieldDeclaration, Optional<String> newFieldDeclaration) {
this.changeStatus = changeStatus;
this.oldFieldDeclaration = oldFieldDeclaration;
this.newFieldDeclaration = newFieldDeclaration;
}
public JApiChangeStatus getChangeStatus() {
return changeStatus;
}
public Optional<String> getOldFieldDeclaration() {
return oldFieldDeclaration;
}
public Optional<String> getNewFieldDeclaration() {
return newFieldDeclaration;
}
}
}

View File

@ -5,6 +5,7 @@
package org.sleuthkit.autopsy.apiupdate;
import java.io.File;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -37,7 +38,7 @@ public class CLIProcessor {
.hasArg(true)
.longOpt("curr-path")
.option("c")
.required(true)
.required(false)
.build();
static Option PREV_VERS_OPT = Option.builder()
@ -46,7 +47,7 @@ public class CLIProcessor {
.hasArg(true)
.longOpt("prev-version")
.option("pv")
.required(true)
.required(false)
.build();
static Option CUR_VERS_OPT = Option.builder()
@ -55,7 +56,7 @@ public class CLIProcessor {
.hasArg(true)
.longOpt("curr-version")
.option("cv")
.required(true)
.required(false)
.build();
static Option SRC_LOC_OPT = Option.builder()
@ -67,15 +68,27 @@ public class CLIProcessor {
.required(true)
.build();
static Option UPDATE_OPT = Option.builder()
.desc("Update source code versions")
.hasArg(false)
.longOpt("update")
.option("u")
.required(false)
.build();
static List<Option> ALL_OPTIONS = Arrays.asList(
PREV_VERS_PATH_OPT,
CUR_VERS_PATH_OPT,
PREV_VERS_OPT,
CUR_VERS_OPT,
SRC_LOC_OPT
SRC_LOC_OPT,
UPDATE_OPT
);
static 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";
private static Options getCliOptions(List<Option> opts) {
Options toRet = new Options();
@ -112,15 +125,19 @@ public class CLIProcessor {
CommandLine helpCmd = parser.parse(HELP_OPTIONS, args, true);
boolean isHelp = helpCmd.hasOption(HELP_OPT);
if (isHelp) {
return new CLIArgs(null, null, null, null, null, true);
return new CLIArgs(null, null, null, null, null, false, true);
}
CommandLine cmd = parser.parse(CLI_OPTIONS, args);
String curVers = cmd.getOptionValue(CUR_VERS_OPT);
String prevVers = cmd.getOptionValue(PREV_VERS_OPT);
String curVersPath = cmd.getOptionValue(CUR_VERS_PATH_OPT);
String curVers = cmd.hasOption(CUR_VERS_OPT) ? cmd.getOptionValue(CUR_VERS_OPT) : DEFAULT_CURR_VERSION;
String prevVers = cmd.hasOption(PREV_VERS_OPT) ? cmd.getOptionValue(PREV_VERS_OPT) : DEFAULT_PREV_VERSION;
String curVersPath = cmd.hasOption(CUR_VERS_PATH_OPT)
? cmd.getOptionValue(CUR_VERS_PATH_OPT)
: Paths.get(cmd.getOptionValue(SRC_LOC_OPT), BUILD_REL_PATH).toString();
String prevVersPath = cmd.getOptionValue(PREV_VERS_PATH_OPT);
String srcPath = cmd.getOptionValue(SRC_LOC_OPT);
boolean makeUpdate = cmd.hasOption(UPDATE_OPT);
File curVersFile = new File(curVersPath);
File prevVersFile = new File(prevVersPath);
File srcPathFile = new File(srcPath);
@ -137,7 +154,7 @@ public class CLIProcessor {
throw new ParseException("No directory found at " + srcPathFile.getAbsolutePath());
}
return new CLIArgs(curVers, prevVers, curVersFile, prevVersFile, srcPathFile, false);
return new CLIArgs(curVers, prevVers, curVersFile, prevVersFile, srcPathFile, makeUpdate, false);
}
public static class CLIArgs {
@ -148,14 +165,16 @@ public class CLIProcessor {
private final File previousVersPath;
private final boolean isHelp;
private final File srcPath;
private final boolean makeUpdate;
public CLIArgs(String currentVersion, String previousVersion, File currentVersPath, File previousVersPath, File srcPath, boolean isHelp) {
public CLIArgs(String currentVersion, String previousVersion, File currentVersPath, File previousVersPath, File srcPath, boolean makeUpdate, boolean isHelp) {
this.currentVersion = currentVersion;
this.previousVersion = previousVersion;
this.currentVersPath = currentVersPath;
this.previousVersPath = previousVersPath;
this.srcPath = srcPath;
this.isHelp = isHelp;
this.makeUpdate = makeUpdate;
}
public String getCurrentVersion() {
@ -178,6 +197,10 @@ public class CLIProcessor {
return isHelp;
}
public boolean isMakeUpdate() {
return makeUpdate;
}
public File getSrcPath() {
return srcPath;
}

View File

@ -1,662 +0,0 @@
package org.sleuthkit.autopsy.apiupdate;
import japicmp.cli.CliParser;
import japicmp.config.Options;
import japicmp.model.*;
import japicmp.model.JApiAnnotationElementValue.Type;
import static japicmp.model.JApiChangeStatus.MODIFIED;
import static japicmp.model.JApiChangeStatus.NEW;
import static japicmp.model.JApiChangeStatus.REMOVED;
import static japicmp.model.JApiChangeStatus.UNCHANGED;
import japicmp.output.OutputFilter;
import japicmp.output.OutputGenerator;
import javassist.bytecode.annotation.MemberValue;
import java.util.Comparator;
import java.util.List;
import static japicmp.util.GenericTemplateHelper.haveGenericTemplateInterfacesChanges;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javassist.CtClass;
import org.apache.commons.lang3.StringUtils;
import org.sleuthkit.autopsy.apiupdate.ApiChangeDTO.FieldChangeDTO;
import org.sleuthkit.autopsy.apiupdate.ApiChangeDTO.InterfaceChangeDTO;
import org.sleuthkit.autopsy.apiupdate.ApiChangeDTO.SuperclassChangeDTO;
// taken from https://raw.githubusercontent.com/siom79/japicmp/master/japicmp/src/main/java/japicmp/output/stdout/StdoutOutputGenerator.java
public class ChangeOutputGenerator extends OutputGenerator<ApiChangeDTO> {
static final String NO_CHANGES = "No changes.";
static final String WARNING = "WARNING";
public ChangeOutputGenerator(Options options, List<JApiClass> jApiClasses) {
super(options, jApiClasses);
}
@Override
public String generate() {
OutputFilter outputFilter = new OutputFilter(options);
outputFilter.filter(jApiClasses);
StringBuilder sb = new StringBuilder();
sb.append(options.getDifferenceDescription()).append('\n');
if (options.getIgnoreMissingClasses().isIgnoreAllMissingClasses()) {
sb.append(WARNING).append(": You are using the option '").append(CliParser.IGNORE_MISSING_CLASSES)
.append("', i.e. superclasses and interfaces that could not be found on the classpath are ignored.")
.append(" Hence changes caused by these superclasses and interfaces are not reflected in the output.\n");
} else if (options.getIgnoreMissingClasses().getIgnoreMissingClassRegularExpression().size() > 0) {
sb.append(WARNING).append(": You have ignored certain classes, i.e. superclasses and interfaces that could not ")
.append("be found on the classpath are ignored. Hence changes caused by these superclasses and interfaces are not reflected in the output.\n");
}
if (jApiClasses.size() > 0) {
for (JApiClass jApiClass : jApiClasses) {
processClass(sb, jApiClass);
processConstructors(sb, jApiClass);
processMethods(sb, jApiClass);
processAnnotations(sb, jApiClass, 1);
}
} else {
sb.append(NO_CHANGES);
}
return sb.toString();
}
private void processAnnotations(StringBuilder sb, JApiHasAnnotations jApiClass, int numberofTabs) {
List<JApiAnnotation> annotations = jApiClass.getAnnotations();
for (JApiAnnotation jApiAnnotation : annotations) {
appendAnnotation(sb, signs(jApiAnnotation), jApiAnnotation, numberofTabs);
List<JApiAnnotationElement> elements = jApiAnnotation.getElements();
for (JApiAnnotationElement jApiAnnotationElement : elements) {
appendAnnotationElement(sb, signs(jApiAnnotationElement), jApiAnnotationElement, numberofTabs + 1);
}
}
}
private void processConstructors(StringBuilder sb, JApiClass jApiClass) {
List<JApiConstructor> constructors = jApiClass.getConstructors();
for (JApiConstructor jApiConstructor : constructors) {
appendBehavior(sb, signs(jApiConstructor), jApiConstructor, "CONSTRUCTOR:");
processAnnotations(sb, jApiConstructor, 2);
processExceptions(sb, jApiConstructor, 2);
processGenericTemplateChanges(sb, jApiConstructor, 2);
}
}
private void processMethods(StringBuilder sb, JApiClass jApiClass) {
List<JApiMethod> methods = jApiClass.getMethods();
for (JApiMethod jApiMethod : methods) {
appendBehavior(sb, signs(jApiMethod), jApiMethod, "METHOD:");
processAnnotations(sb, jApiMethod, 2);
processExceptions(sb, jApiMethod, 2);
processGenericTemplateChanges(sb, jApiMethod, 2);
}
}
private void processExceptions(StringBuilder sb, JApiBehavior jApiBehavior, int indent) {
for (JApiException exception : jApiBehavior.getExceptions()) {
appendException(sb, signs(exception), exception, indent);
}
}
private void appendException(StringBuilder sb, String signs, JApiException jApiException, int indent) {
sb.append(tabs(indent)).append(signs).append(" ").append(jApiException.getChangeStatus()).append(" EXCEPTION: ").append(jApiException.getName()).append("\n");
}
private void processClass(StringBuilder sb, JApiClass jApiClass) {
appendClass(sb, signs(jApiClass), jApiClass);
}
private void appendBehavior(StringBuilder sb, String signs, JApiBehavior jApiBehavior, String classMemberType) {
sb.append("\t").append(signs).append(" ").append(jApiBehavior.getChangeStatus()).append(" ").append(classMemberType).append(" ")
.append(accessModifierAsString(jApiBehavior)).append(abstractModifierAsString(jApiBehavior)).append(staticModifierAsString(jApiBehavior))
.append(finalModifierAsString(jApiBehavior)).append(syntheticModifierAsString(jApiBehavior)).append(bridgeModifierAsString(jApiBehavior))
.append(returnType(jApiBehavior)).append(jApiBehavior.getName()).append("(");
int paramCount = 0;
for (JApiParameter jApiParameter : jApiBehavior.getParameters()) {
if (paramCount > 0) {
sb.append(", ");
}
sb.append(jApiParameter.getType());
appendGenericTypes(sb, jApiParameter);
paramCount++;
}
sb.append(")\n");
}
private void appendGenericTypes(StringBuilder sb, JApiHasGenericTypes jApiHasGenericTypes) {
if (!jApiHasGenericTypes.getNewGenericTypes().isEmpty() || !jApiHasGenericTypes.getOldGenericTypes().isEmpty()) {
if (jApiHasGenericTypes instanceof JApiCompatibility) {
List<JApiCompatibilityChange> compatibilityChanges = ((JApiCompatibility) jApiHasGenericTypes).getCompatibilityChanges();
appendGenericTypesForCompatibilityChanges(sb, jApiHasGenericTypes, compatibilityChanges);
}
}
}
private void appendGenericTypesForCompatibilityChanges(StringBuilder sb, JApiHasGenericTypes jApiHasGenericTypes, List<JApiCompatibilityChange> compatibilityChanges) {
if (compatibilityChanges.contains(JApiCompatibilityChange.METHOD_PARAMETER_GENERICS_CHANGED)
|| compatibilityChanges.contains(JApiCompatibilityChange.METHOD_RETURN_TYPE_GENERICS_CHANGED)
|| compatibilityChanges.contains(JApiCompatibilityChange.FIELD_GENERICS_CHANGED)
|| compatibilityChanges.contains(JApiCompatibilityChange.CLASS_GENERIC_TEMPLATE_GENERICS_CHANGED)) {
appendGenericTypes(sb, false, jApiHasGenericTypes.getNewGenericTypes());
appendGenericTypes(sb, true, jApiHasGenericTypes.getOldGenericTypes());
} else {
if (!jApiHasGenericTypes.getNewGenericTypes().isEmpty()) {
appendGenericTypes(sb, false, jApiHasGenericTypes.getNewGenericTypes());
}
if (!jApiHasGenericTypes.getOldGenericTypes().isEmpty()) {
appendGenericTypes(sb, false, jApiHasGenericTypes.getOldGenericTypes());
}
}
}
private void appendGenericTypes(StringBuilder sb, boolean withChangeInParenthesis, List<JApiGenericType> genericTypes) {
if (!genericTypes.isEmpty()) {
if (withChangeInParenthesis) {
sb.append("(<- ");
}
sb.append("<");
int count = 0;
for (JApiGenericType genericType : genericTypes) {
if (count > 0) {
sb.append(",");
}
appendGenericType(sb, genericType);
if (!genericType.getGenericTypes().isEmpty()) {
appendGenericTypes(sb, false, genericType.getGenericTypes());
}
count++;
}
sb.append(">");
if (withChangeInParenthesis) {
sb.append(")");
}
}
}
private void appendGenericType(StringBuilder sb, JApiGenericType jApiGenericType) {
if (jApiGenericType.getGenericWildCard() == JApiGenericType.JApiGenericWildCard.NONE) {
sb.append(jApiGenericType.getType());
} else if (jApiGenericType.getGenericWildCard() == JApiGenericType.JApiGenericWildCard.UNBOUNDED) {
sb.append("?");
} else if (jApiGenericType.getGenericWildCard() == JApiGenericType.JApiGenericWildCard.EXTENDS) {
sb.append("? extends ").append(jApiGenericType.getType());
} else if (jApiGenericType.getGenericWildCard() == JApiGenericType.JApiGenericWildCard.SUPER) {
sb.append("? super ").append(jApiGenericType.getType());
}
}
private String returnType(JApiBehavior jApiBehavior) {
StringBuilder sb = new StringBuilder();
if (jApiBehavior instanceof JApiMethod) {
JApiMethod method = (JApiMethod) jApiBehavior;
JApiReturnType jApiReturnType = method.getReturnType();
if (jApiReturnType.getChangeStatus() == JApiChangeStatus.UNCHANGED) {
sb.append(jApiReturnType.getNewReturnType());
appendGenericTypes(sb, jApiReturnType);
sb.append(" ");
} else if (jApiReturnType.getChangeStatus() == JApiChangeStatus.MODIFIED) {
sb.append(jApiReturnType.getNewReturnType());
appendGenericTypes(sb, jApiReturnType);
sb.append(" (<-");
sb.append(jApiReturnType.getOldReturnType());
appendGenericTypes(sb, jApiReturnType);
sb.append(") ");
} else if (jApiReturnType.getChangeStatus() == JApiChangeStatus.NEW) {
sb.append(jApiReturnType.getNewReturnType());
appendGenericTypes(sb, jApiReturnType);
sb.append(" ");
} else {
sb.append(jApiReturnType.getOldReturnType());
appendGenericTypes(sb, jApiReturnType);
sb.append(" ");
}
}
return sb.toString();
}
private void appendAnnotation(StringBuilder sb, String signs, JApiAnnotation jApiAnnotation, int numberOfTabs) {
sb.append(String.format("%s%s %s ANNOTATION: %s\n", tabs(numberOfTabs), signs, jApiAnnotation.getChangeStatus(), jApiAnnotation.getFullyQualifiedName()));
}
private void appendAnnotationElement(StringBuilder sb, String signs, JApiAnnotationElement jApiAnnotationElement, int numberOfTabs) {
sb.append(String.format("%s%s %s ELEMENT: %s=", tabs(numberOfTabs), signs, jApiAnnotationElement.getChangeStatus(), jApiAnnotationElement.getName()));
Optional<MemberValue> oldValue = jApiAnnotationElement.getOldValue();
Optional<MemberValue> newValue = jApiAnnotationElement.getNewValue();
if (oldValue.isPresent() && newValue.isPresent()) {
if (jApiAnnotationElement.getChangeStatus() == JApiChangeStatus.UNCHANGED) {
sb.append(elementValueList2String(jApiAnnotationElement.getNewElementValues()));
} else if (jApiAnnotationElement.getChangeStatus() == JApiChangeStatus.REMOVED) {
sb.append(String.format("%s (-)", elementValueList2String(jApiAnnotationElement.getOldElementValues())));
} else if (jApiAnnotationElement.getChangeStatus() == JApiChangeStatus.NEW) {
sb.append(String.format("%s (+)", elementValueList2String(jApiAnnotationElement.getNewElementValues())));
} else if (jApiAnnotationElement.getChangeStatus() == JApiChangeStatus.MODIFIED) {
sb.append(String.format("%s (<- %s)", elementValueList2String(jApiAnnotationElement.getNewElementValues()), elementValueList2String(jApiAnnotationElement.getOldElementValues())));
}
} else if (!oldValue.isPresent() && newValue.isPresent()) {
sb.append(String.format("%s (+)", elementValueList2String(jApiAnnotationElement.getNewElementValues())));
} else if (oldValue.isPresent() && !newValue.isPresent()) {
sb.append(String.format("%s (-)", elementValueList2String(jApiAnnotationElement.getOldElementValues())));
} else {
sb.append(" n.a.");
}
sb.append("\n");
}
private void appendClass(StringBuilder sb, String signs, JApiClass jApiClass) {
sb.append(signs).append(" ").append(jApiClass.getChangeStatus()).append(" ")
.append(processClassType(jApiClass)).append(": ")
.append(accessModifierAsString(jApiClass))
.append(abstractModifierAsString(jApiClass))
.append(staticModifierAsString(jApiClass))
.append(finalModifierAsString(jApiClass))
.append(syntheticModifierAsString(jApiClass))
.append(jApiClass.getFullyQualifiedName()).append(" ")
.append(javaObjectSerializationStatus(jApiClass)).append("\n");
processGenericTemplateChanges(sb, jApiClass, 1);
processInterfaceChanges(sb, jApiClass);
processSuperclassChanges(sb, jApiClass);
processFieldChanges(sb, jApiClass);
}
private String getClassString(JApiClass clz, boolean oldClass) {
// TODO serial version id
// TODO annotations
String accessModifier = str(get(clz.getAccessModifier(), oldClass).orElse(null));
String abstractModifier = str(get(clz.getAbstractModifier(), oldClass).orElse(null));
String staticModifier = str(get(clz.getStaticModifier(), oldClass).orElse(null));
String finalModifier = str(get(clz.getFinalModifier(), oldClass).orElse(null));
String syntheticModifier = str(get(clz.getSyntheticModifier(), oldClass).orElse(null));
String type = str(clz.getClassType(), oldClass);
String name = clz.getFullyQualifiedName();
String genericModifier = strGeneric(clz.getGenericTemplates(), oldClass);
String implementsModifier = strImplements(clz.getInterfaces(), oldClass);
String extendsModifier = str(clz.getSuperclass(), oldClass);
return Stream.of(
accessModifier,
abstractModifier,
staticModifier,
finalModifier,
syntheticModifier,
type,
name + (StringUtils.isBlank(genericModifier) ? "" : genericModifier),
implementsModifier,
extendsModifier)
.filter(StringUtils::isNotBlank)
.collect(Collectors.joining(" "));
}
private String str(JApiSuperclass supClz, boolean oldClass) {
Optional<CtClass> ctClass = convert(oldClass ? supClz.getOldSuperclass() : supClz.getNewSuperclass());
String supClassExt = ctClass.map(ctClz -> str(ctClz)).orElse(null);
return StringUtils.isBlank(supClassExt) ? null : "extends " + supClassExt;
}
private String strImplements(List<JApiImplementedInterface> interfaces, boolean oldClass) {
if (interfaces.isEmpty()) {
return null;
}
String interfaceStr = interfaces.stream()
.filter(i -> (oldClass && i.getChangeStatus() == NEW) || (!oldClass && i.getChangeStatus() == REMOVED))
.map(i -> str(i.getCtClass()))
.collect(Collectors.joining(", "));
return StringUtils.isNotBlank(interfaceStr) ? "implements " + interfaceStr : null;
}
private String str(CtClass ctClass) {
return ctClass.getName() + (StringUtils.isBlank(ctClass.getGenericSignature()) ? "" : "<" + ctClass.getGenericSignature() + ">");
}
private String str(JApiClassType classType, boolean oldClass) {
return Optional.ofNullable(classType)
.flatMap(ct -> convert(oldClass ? ct.getOldTypeOptional() : ct.getNewTypeOptional()))
.map(ct -> ct.name().toLowerCase())
.orElse("class");
}
private String strGeneric(List<JApiGenericTemplate> templates, boolean oldClass) {
if (templates.isEmpty()) {
return null;
}
String genericParams = templates.stream()
.map(t -> convert(oldClass ?
str(t.getName(), convert(t.getOldTypeOptional()), t.getOldInterfaceTypes()) :
str(t.getName(), convert(t.getNewTypeOptional()), t.getNewInterfaceTypes())
)
.filter(Optional::isPresent)
.map(t -> t.get())
.collect(Collectors.joining(", "));
return StringUtils.isBlank(genericParams)
? null
: "<" + genericParams + ">";
}
private String strGeneric(String name, Optional<String> mainType, List<JApiGenericType> genericType) {
}
// jApiGenericTemplate.getName() + ":" + interfaceTypes.join( "&")
//
// private List<GenericTemplateChangeDTO> getGenericTemplateChanges(JApiHasGenericTemplates jApiHasGenericTemplates) {
// if (!genericTemplates.isEmpty()) {
// sb.append(tabs(numberOfTabs)).append("GENERIC TEMPLATES: ");
// genericTemplates.sort(Comparator.comparing(JApiGenericTemplate::getName));
// int count = 0;
// for (JApiGenericTemplate jApiGenericTemplate : genericTemplates) {
// if (count > 0) {
// sb.append(", ");
// }
// count++;
// sb.append(signs(jApiGenericTemplate));
// if (sb.charAt(sb.length() - 1) != ' ') {
// sb.append(" ");
// }
// sb.append(jApiGenericTemplate.getName()).append(":");
// JApiChangeStatus changeStatus = jApiGenericTemplate.getChangeStatus();
// if (changeStatus == JApiChangeStatus.NEW || changeStatus == JApiChangeStatus.UNCHANGED) {
// sb.append(jApiGenericTemplate.getNewType());
// if (jApiGenericTemplate instanceof JApiCompatibility) {
// appendGenericTypesForCompatibilityChanges(sb, jApiGenericTemplate, ((JApiCompatibility) jApiHasGenericTemplates).getCompatibilityChanges());
// }
// } else if (changeStatus == JApiChangeStatus.REMOVED) {
// sb.append(jApiGenericTemplate.getOldType());
// if (jApiGenericTemplate instanceof JApiCompatibility) {
// appendGenericTypesForCompatibilityChanges(sb, jApiGenericTemplate, ((JApiCompatibility) jApiHasGenericTemplates).getCompatibilityChanges());
// }
// } else {
// sb.append(jApiGenericTemplate.getNewType());
// appendGenericTypes(sb, false, jApiGenericTemplate.getNewGenericTypes());
// sb.append(" (<-").append(jApiGenericTemplate.getOldType());
// appendGenericTypes(sb, false, jApiGenericTemplate.getOldGenericTypes());
// sb.append(")");
// }
// if (!jApiGenericTemplate.getOldInterfaceTypes().isEmpty() || !jApiGenericTemplate.getNewInterfaceTypes().isEmpty()) {
// if (haveGenericTemplateInterfacesChanges(jApiGenericTemplate.getOldInterfaceTypes(), jApiGenericTemplate.getNewInterfaceTypes())) {
// appendGenericTemplatesInterfaces(sb, jApiGenericTemplate, false, true);
// sb.append(" (<-");
// appendGenericTemplatesInterfaces(sb, jApiGenericTemplate, true, false);
// sb.append(")");
// } else {
// appendGenericTemplatesInterfaces(sb, jApiGenericTemplate, false, true);
// }
// }
// }
// sb.append("\n");
// }
// }
private void appendGenericTemplatesInterfaces(StringBuilder sb, JApiGenericTemplate jApiGenericTemplate, boolean printOld, boolean printNew) {
if (printOld) {
for (JApiGenericType jApiGenericType : jApiGenericTemplate.getOldInterfaceTypes()) {
sb.append(" & ");
sb.append(jApiGenericType.getType());
appendGenericTypes(sb, false, jApiGenericType.getGenericTypes());
}
}
if (printNew) {
for (JApiGenericType jApiGenericType : jApiGenericTemplate.getNewInterfaceTypes()) {
sb.append(" & ");
sb.append(jApiGenericType.getType());
appendGenericTypes(sb, false, jApiGenericType.getGenericTypes());
}
}
}
private FieldChangeDTO getFieldChange(JApiField field) {
return new FieldChangeDTO(
field.getChangeStatus(),
Optional.ofNullable(getFieldString(field, true)),
Optional.ofNullable(getFieldString(field, false)));
}
private String getFieldString(JApiField field, boolean oldField) {
String accessModifier = str(get(field.getAccessModifier(), oldField).orElse(null));
String staticModifier = str(get(field.getStaticModifier(), oldField).orElse(null));
String finalModifier = str(get(field.getFinalModifier(), oldField).orElse(null));
String syntheticModifier = str(get(field.getSyntheticModifier(), oldField).orElse(null));
String fieldType = strOpt(field.getType(), oldField).orElse(null);
String name = field.getName();
return Stream.of(accessModifier, staticModifier, finalModifier, syntheticModifier, fieldType, name)
.filter(StringUtils::isNotBlank)
.collect(Collectors.joining(" "));
}
private String str(AccessModifier modifier) {
if (modifier == null || modifier == AccessModifier.PACKAGE_PROTECTED) {
return null;
} else {
return modifier.name().toLowerCase();
}
}
private String str(StaticModifier modifier) {
return (modifier == StaticModifier.STATIC)
? "static"
: null;
}
private String str(FinalModifier modifier) {
return (modifier == FinalModifier.FINAL)
? "final"
: null;
}
private String str(SyntheticModifier modifier) {
return (modifier == SyntheticModifier.SYNTHETIC)
? "synthetic"
: null;
}
private String str(AbstractModifier modifier) {
return (modifier == AbstractModifier.ABSTRACT)
? "abstract"
: null;
}
private Optional<String> strOpt(JApiType tp, boolean oldField) {
return oldField ? convert(tp.getOldTypeOptional()) : convert(tp.getNewTypeOptional());
}
private <T> Optional<T> get(JApiModifier<T> modifier, boolean oldField) {
return oldField ? convert(modifier.getOldModifier()) : convert(modifier.getNewModifier());
}
// private Optional<SuperclassChangeDTO> getSuperClassChange(JApiSuperclass jApiSuperclass) {
// return jApiSuperclass.getChangeStatus() != JApiChangeStatus.UNCHANGED
// ? Optional.ofNullable(new SuperclassChangeDTO(
// convert(jApiSuperclass.getOldSuperclassName()),
// convert(jApiSuperclass.getNewSuperclassName())))
// : Optional.empty();
// }
//
// private List<InterfaceChangeDTO> getInterfaceChanges(JApiClass jApiClass) {
// return jApiClass.getInterfaces().stream()
// .filter(intfc -> intfc.getChangeStatus() != JApiChangeStatus.UNCHANGED)
// .map(intfc -> new InterfaceChangeDTO(intfc.getChangeStatus(), intfc.getFullyQualifiedName()))
// .collect(Collectors.toList());
// }
private <T> Optional<T> convert(japicmp.util.Optional<T> apiOpt) {
T val = apiOpt.or((T) null);
return java.util.Optional.ofNullable(val);
}
//
// private void processClassFileFormatVersionChanges(StringBuilder sb, JApiClass jApiClass) {
// JApiClassFileFormatVersion classFileFormatVersion = jApiClass.getClassFileFormatVersion();
// sb.append(tabs(1))
// .append(signs(classFileFormatVersion))
// .append(" CLASS FILE FORMAT VERSION: ");
// if (classFileFormatVersion.getMajorVersionNew() != -1 && classFileFormatVersion.getMinorVersionNew() != -1) {
// sb.append(classFileFormatVersion.getMajorVersionNew()).append(".").append(classFileFormatVersion.getMinorVersionNew());
// } else {
// sb.append("n.a.");
// }
// sb.append(" <- ");
// if (classFileFormatVersion.getMajorVersionOld() != -1 && classFileFormatVersion.getMinorVersionOld() != -1) {
// sb.append(classFileFormatVersion.getMajorVersionOld()).append(".").append(classFileFormatVersion.getMinorVersionOld());
// } else {
// sb.append("n.a.");
// }
// sb.append("\n");
// }
// private String processClassType(JApiClass jApiClass) {
// JApiClassType classType = jApiClass.getClassType();
// switch (classType.getChangeStatus()) {
// case NEW:
// return classType.getNewType();
// case REMOVED:
// return classType.getOldType();
// case MODIFIED:
// return classType.getNewType() + " (<- " + classType.getOldType() + ") ";
// case UNCHANGED:
// return classType.getOldType();
// }
// return "n.a.";
// }
//
// // TODO do we need this
// private String getJavaObjectSerializationStatus(JApiClass jApiClass) {
// return jApiClass.getJavaObjectSerializationCompatible().getDescription();
// }
// private String elementValueList2String(List<JApiAnnotationElementValue> values) {
// StringBuilder sb = new StringBuilder();
// for (JApiAnnotationElementValue value : values) {
// if (sb.length() > 0) {
// sb.append(",");
// }
// if (value.getName().isPresent()) {
// sb.append(value.getName().get()).append("=");
// }
// if (value.getType() != Type.Array && value.getType() != Type.Annotation) {
// if (value.getType() == Type.Enum) {
// sb.append(value.getFullyQualifiedName()).append(".").append(value.getValueString());
// } else {
// sb.append(value.getValueString());
// }
// } else {
// if (value.getType() == Type.Array) {
// sb.append("{").append(elementValueList2String(value.getValues())).append("}");
// } else if (value.getType() == Type.Annotation) {
// sb.append("@").append(value.getFullyQualifiedName()).append("(").append(elementValueList2String(value.getValues())).append(")");
// }
// }
// }
// return sb.toString();
// }
//
// private String tabs(int numberOfTabs) {
// if (numberOfTabs <= 0) {
// return "";
// } else if (numberOfTabs == 1) {
// return "\t";
// } else if (numberOfTabs == 2) {
// return "\t\t";
// } else {
// StringBuilder sb = new StringBuilder();
// for (int i = 0; i < numberOfTabs; i++) {
// sb.append("\t");
// }
// return sb.toString();
// }
// }
// private String signs(JApiHasChangeStatus hasChangeStatus) {
// JApiChangeStatus changeStatus = hasChangeStatus.getChangeStatus();
// String retVal = "???";
// switch (changeStatus) {
// case UNCHANGED:
// retVal = "===";
// break;
// case NEW:
// retVal = "+++";
// break;
// case REMOVED:
// retVal = "---";
// break;
// case MODIFIED:
// retVal = "***";
// break;
// }
// boolean binaryCompatible = true;
// boolean sourceCompatible = true;
// if (hasChangeStatus instanceof JApiCompatibility) {
// JApiCompatibility jApiCompatibility = (JApiCompatibility) hasChangeStatus;
// binaryCompatible = jApiCompatibility.isBinaryCompatible();
// sourceCompatible = jApiCompatibility.isSourceCompatible();
// }
// if (binaryCompatible) {
// if (sourceCompatible) {
// retVal += " ";
// } else {
// retVal += "*";
// }
// } else {
// retVal += "!";
// }
// return retVal;
// }
//
// private String bridgeModifierAsString(JApiHasBridgeModifier modifier) {
// JApiModifier<BridgeModifier> bridgeModifier = modifier.getBridgeModifier();
// return modifierAsString(bridgeModifier, BridgeModifier.NON_BRIDGE);
// }
// private <T> String modifierAsString(JApiModifier<T> modifier, T notPrintValue) {
// if (modifier.getOldModifier().isPresent() && modifier.getNewModifier().isPresent()) {
// if (modifier.getChangeStatus() == JApiChangeStatus.MODIFIED) {
// return modifier.getNewModifier().get() + " (<- " + modifier.getOldModifier().get() + ") ";
// } else if (modifier.getChangeStatus() == JApiChangeStatus.NEW) {
// if (modifier.getNewModifier().get() != notPrintValue) {
// return modifier.getNewModifier().get() + "(+) ";
// }
// } else if (modifier.getChangeStatus() == JApiChangeStatus.REMOVED) {
// if (modifier.getOldModifier().get() != notPrintValue) {
// return modifier.getOldModifier().get() + "(-) ";
// }
// } else {
// if (modifier.getNewModifier().get() != notPrintValue) {
// return modifier.getNewModifier().get() + " ";
// }
// }
// } else if (modifier.getOldModifier().isPresent()) {
// if (modifier.getOldModifier().get() != notPrintValue) {
// return modifier.getOldModifier().get() + "(-) ";
// }
// } else if (modifier.getNewModifier().isPresent()) {
// if (modifier.getNewModifier().get() != notPrintValue) {
// return modifier.getNewModifier().get() + "(+) ";
// }
// }
// return "";
// }
// private String fieldTypeChangeAsString(JApiField field) {
// JApiType type = field.getType();
// if (type.getOldTypeOptional().isPresent() && type.getNewTypeOptional().isPresent()) {
// if (type.getChangeStatus() == JApiChangeStatus.MODIFIED) {
// return type.getNewTypeOptional().get() + " (<- " + type.getOldTypeOptional().get() + ")";
// } else if (type.getChangeStatus() == JApiChangeStatus.NEW) {
// return type.getNewTypeOptional().get() + "(+)";
// } else if (type.getChangeStatus() == JApiChangeStatus.REMOVED) {
// return type.getOldTypeOptional().get() + "(-)";
// } else {
// return type.getNewTypeOptional().get();
// }
// } else if (type.getOldTypeOptional().isPresent() && !type.getNewTypeOptional().isPresent()) {
// return type.getOldTypeOptional().get();
// } else if (!type.getOldTypeOptional().isPresent() && type.getNewTypeOptional().isPresent()) {
// return type.getNewTypeOptional().get();
// }
// return "n.a.";
// }
}

View File

@ -4,10 +4,15 @@
package org.sleuthkit.autopsy.apiupdate;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.cli.ParseException;
import org.sleuthkit.autopsy.apiupdate.APIDiff.ComparisonRecord;
import org.sleuthkit.autopsy.apiupdate.CLIProcessor.CLIArgs;
import org.sleuthkit.autopsy.apiupdate.ModuleUpdates.ModuleVersionNumbers;
/**
*
@ -15,6 +20,8 @@ import org.sleuthkit.autopsy.apiupdate.CLIProcessor.CLIArgs;
*/
public class Main {
private static final Logger LOGGER = Logger.getLogger(Main.class.getName());
public static void main(String[] args) {
args = "-c C:\\Users\\gregd\\Desktop\\apidiff\\new -p C:\\Users\\gregd\\Desktop\\apidiff\\old -cv 4.21.0 -pv 4.20.0 -s C:\\Users\\gregd\\Documents\\Source\\autopsy".split(" ");
CLIArgs cliArgs;
@ -29,67 +36,69 @@ public class Main {
System.exit(-1);
return;
}
// Map<String, ModuleVersionNumbers> versNums = Stream.of(
// new ModuleVersionNumbers(
// "org.sleuthkit.autopsy.core",
// new ModuleUpdates.SemVer(1,2,3),
// 4,
// new ReleaseVal("org.sleuthkit.autopsy.core", 5)),
// new ModuleVersionNumbers(
// "org.sleuthkit.autopsy.corelibs",
// new ModuleUpdates.SemVer(6,7,8),
// 9,
// new ReleaseVal("org.sleuthkit.autopsy.corelibs", 10)))
// .collect(Collectors.toMap(v -> v.getModuleName(), v -> v, (v1, v2) -> v1));
//
// ModuleUpdates.setVersions(cliArgs.getSrcPath(), versNums);
Map<String, ModuleVersionNumbers> newVersionNumMapping = new HashMap<>();
for (String commonJarFileName : APIDiff.getCommonJars(cliArgs.getPreviousVersPath(), cliArgs.getCurrentVersPath())) {
try {
// ModuleVersionNumbers m = ModuleUpdates.getVersionsFromJar(cliArgs.getPreviousVersPath().toPath().resolve(commonJarFileName).toFile());
// System.out.println(MessageFormat.format("release: {0}, spec: {1}, implementation: {2}", m.getRelease().getFullReleaseStr(), m.getSpec().getSemVerStr(), m.getImplementation()));
APIDiff.getComparison(
cliArgs.getPreviousVersion(),
cliArgs.getCurrentVersion(),
ModuleVersionNumbers prevVersionNums = ModuleUpdates.getVersionsFromJar(cliArgs.getPreviousVersPath().toPath().resolve(commonJarFileName).toFile());
ComparisonRecord record = APIDiff.getComparison(
cliArgs.getPreviousVersion(),
cliArgs.getCurrentVersion(),
cliArgs.getPreviousVersPath().toPath().resolve(commonJarFileName).toFile(),
cliArgs.getCurrentVersPath().toPath().resolve(commonJarFileName).toFile());
ModuleVersionNumbers projectedVersionNums = ModuleUpdates.getModuleVersionUpdate(prevVersionNums, record.getChangeType());
outputDiff(commonJarFileName, record, prevVersionNums, projectedVersionNums);
newVersionNumMapping.put(commonJarFileName, projectedVersionNums);
} catch (IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
if (cliArgs.isMakeUpdate()) {
ModuleUpdates.setVersions(cliArgs.getSrcPath(), newVersionNumMapping);
}
// for (String commonJarFileName : getCommonJars(cliArgs.getPreviousVersPath(), cliArgs.getCurrentVersPath())) {
//// getComparison(
//// cliArgs.getPreviousVersion(),
//// cliArgs.getCurrentVersion(),
//// cliArgs.getPreviousVersPath().toPath().resolve(commonJarFileName).toFile(),
//// cliArgs.getCurrentVersPath().toPath().resolve(commonJarFileName).toFile());
// try {
// Set<String> pubPackages = getPublicPackages(cliArgs.getPreviousVersPath().toPath().resolve(commonJarFileName).toFile());
// System.out.println(pubPackages);
// } catch (IOException ex) {
// Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
// } catch (IllegalStateException ex) {
// Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
// }
// }
}
private static void mainRun() {
// get public API diff's, for each jar
// limit to public packages
// one of the following:
// generate text output of difference
// update version numbers in manifest file/references accordingly
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",
commonJarFileName,
record.getChangeType(),
prevVersionNums.getRelease().getFullReleaseStr(),
prevVersionNums.getSpec().getSemVerStr(),
prevVersionNums.getImplementation(),
projectedVersionNums.getRelease().getFullReleaseStr(),
projectedVersionNums.getSpec().getSemVerStr(),
projectedVersionNums.getImplementation(),
record.getOnlyPrevApiPackages(),
record.getOnlyCurrApiPackages(),
record.getHumanReadableApiChange()
));
}
}

View File

@ -155,7 +155,47 @@ public class ModuleUpdates {
return new ModuleVersionNumbers(jarFile.getName(), specSemVer, implementation, release);
}
private static void updateVersions() {
static ModuleVersionNumbers getModuleVersionUpdate(ModuleVersionNumbers prev, PublicApiChangeType apiChangeType) {
switch (apiChangeType) {
case NONE:
return new ModuleVersionNumbers(
prev.getModuleName(),
prev.getSpec(),
prev.getImplementation() + 1,
prev.getRelease()
);
case COMPATIBLE_CHANGE:
return new ModuleVersionNumbers(
prev.getModuleName(),
new SemVer(
prev.getSpec().getMajor(),
prev.getSpec().getMinor() + 1,
prev.getSpec().getPatch()
),
prev.getImplementation() + 1,
new ReleaseVal(
prev.getRelease().getModuleName(),
prev.getRelease().getReleaseVersion()
)
);
case INCOMPATIBLE_CHANGE:
return new ModuleVersionNumbers(
prev.getModuleName(),
new SemVer(
prev.getSpec().getMajor() + 1,
prev.getSpec().getMinor(),
prev.getSpec().getPatch()
),
prev.getImplementation() + 1,
new ReleaseVal(
prev.getRelease().getModuleName(),
prev.getRelease().getReleaseVersion() == null ? null : prev.getRelease().getReleaseVersion() + 1
)
);
default:
throw new IllegalArgumentException("Unknown api change type: " + apiChangeType);
}
// [specification major/minor/patch, implementation, release]
// assumed defaults???
// NON_COMPATIBLE:

View File

@ -4,10 +4,30 @@
*/
package org.sleuthkit.autopsy.apiupdate;
import java.util.Comparator;
import org.apache.commons.lang3.ObjectUtils;
/**
*
* @author gregd
*/
public enum PublicApiChangeType {
NONE, COMPATIBLE_CHANGE, INCOMPATIBLE_CHANGE;
public enum PublicApiChangeType implements Comparator<PublicApiChangeType> {
NONE(0), COMPATIBLE_CHANGE(1), INCOMPATIBLE_CHANGE(2);
private int level;
PublicApiChangeType(int level) {
this.level = level;
}
public int getLevel() {
return level;
}
@Override
public int compare(PublicApiChangeType o1, PublicApiChangeType o2) {
o1 = ObjectUtils.defaultIfNull(o1, PublicApiChangeType.NONE);
o2 = ObjectUtils.defaultIfNull(o2, PublicApiChangeType.NONE);
return Integer.compare(o1.getLevel(), o2.getLevel());
}
}