From 33654becf28517f9c5c35d65c2f3f451459c53e3 Mon Sep 17 00:00:00 2001 From: Martin Mois Date: Mon, 4 Mar 2024 17:02:22 +0100 Subject: [PATCH 1/7] started HtmlOutputGenerator --- japicmp-testbase/pom.xml | 9 - .../japicmp/exception/JApiCmpException.java | 3 +- .../java/japicmp/output/html/HtmlOutput.java | 13 + .../output/html/HtmlOutputGenerator.java | 625 ++++++++++++++++++ .../html/HtmlOutputGeneratorOptions.java | 24 + .../output/xml/XmlOutputGenerator.java | 35 +- .../main/java/japicmp/util/StringHelper.java | 27 + .../resources/html/annotation-elements.html | 13 + .../src/main/resources/html/annotations.html | 15 + .../src/main/resources/html/class-entry.html | 57 ++ .../html/class-file-format-version.html | 15 + .../resources/html/compatibility-changes.html | 13 + .../src/main/resources/html/constructors.html | 17 + .../src/main/resources/html/exceptions.html | 11 + japicmp/src/main/resources/html/fields.html | 15 + .../resources/html/generic-templates.html | 14 + .../src/main/resources/html/interfaces.html | 13 + .../src/main/resources/html/line-numbers.html | 18 + .../main/resources/html/meta-information.html | 64 ++ .../resources/html/serial-version-uid.html | 15 + .../src/main/resources/html/superclass.html | 13 + .../src/main/resources/html/toc-entry.html | 10 + japicmp/src/main/resources/html/toc.html | 14 + .../output/html/HtmlOutputGeneratorTest.java | 63 ++ pom.xml | 6 + 25 files changed, 1083 insertions(+), 39 deletions(-) create mode 100644 japicmp/src/main/java/japicmp/output/html/HtmlOutput.java create mode 100644 japicmp/src/main/java/japicmp/output/html/HtmlOutputGenerator.java create mode 100644 japicmp/src/main/java/japicmp/output/html/HtmlOutputGeneratorOptions.java create mode 100644 japicmp/src/main/java/japicmp/util/StringHelper.java create mode 100644 japicmp/src/main/resources/html/annotation-elements.html create mode 100644 japicmp/src/main/resources/html/annotations.html create mode 100644 japicmp/src/main/resources/html/class-entry.html create mode 100644 japicmp/src/main/resources/html/class-file-format-version.html create mode 100644 japicmp/src/main/resources/html/compatibility-changes.html create mode 100644 japicmp/src/main/resources/html/constructors.html create mode 100644 japicmp/src/main/resources/html/exceptions.html create mode 100644 japicmp/src/main/resources/html/fields.html create mode 100644 japicmp/src/main/resources/html/generic-templates.html create mode 100644 japicmp/src/main/resources/html/interfaces.html create mode 100644 japicmp/src/main/resources/html/line-numbers.html create mode 100644 japicmp/src/main/resources/html/meta-information.html create mode 100644 japicmp/src/main/resources/html/serial-version-uid.html create mode 100644 japicmp/src/main/resources/html/superclass.html create mode 100644 japicmp/src/main/resources/html/toc-entry.html create mode 100644 japicmp/src/main/resources/html/toc.html create mode 100644 japicmp/src/test/java/japicmp/output/html/HtmlOutputGeneratorTest.java diff --git a/japicmp-testbase/pom.xml b/japicmp-testbase/pom.xml index 549313a7c..b64377954 100644 --- a/japicmp-testbase/pom.xml +++ b/japicmp-testbase/pom.xml @@ -39,15 +39,6 @@ true - - - org.jsoup - jsoup - 1.15.3 - test - - - diff --git a/japicmp/src/main/java/japicmp/exception/JApiCmpException.java b/japicmp/src/main/java/japicmp/exception/JApiCmpException.java index 584d9a9a3..7ef9cddcb 100644 --- a/japicmp/src/main/java/japicmp/exception/JApiCmpException.java +++ b/japicmp/src/main/java/japicmp/exception/JApiCmpException.java @@ -16,7 +16,8 @@ public enum Reason { IllegalState, IllegalArgument, XsltError, - IncompatibleChange + IncompatibleChange, + ResourceNotFound } public JApiCmpException(Reason reason, String msg) { diff --git a/japicmp/src/main/java/japicmp/output/html/HtmlOutput.java b/japicmp/src/main/java/japicmp/output/html/HtmlOutput.java new file mode 100644 index 000000000..0ff32c810 --- /dev/null +++ b/japicmp/src/main/java/japicmp/output/html/HtmlOutput.java @@ -0,0 +1,13 @@ +package japicmp.output.html; + +public class HtmlOutput { + private final String html; + + public HtmlOutput(String html) { + this.html = html; + } + + public String getHtml() { + return html; + } +} diff --git a/japicmp/src/main/java/japicmp/output/html/HtmlOutputGenerator.java b/japicmp/src/main/java/japicmp/output/html/HtmlOutputGenerator.java new file mode 100644 index 000000000..6e17b71ce --- /dev/null +++ b/japicmp/src/main/java/japicmp/output/html/HtmlOutputGenerator.java @@ -0,0 +1,625 @@ +package japicmp.output.html; + +import japicmp.config.Options; +import japicmp.exception.JApiCmpException; +import japicmp.model.*; +import japicmp.output.OutputFilter; +import japicmp.output.OutputGenerator; +import japicmp.util.Streams; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static japicmp.util.StringHelper.filtersAsString; + +public class HtmlOutputGenerator extends OutputGenerator { + + private final HtmlOutputGeneratorOptions htmlOutputGeneratorOptions; + private final static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); + + public HtmlOutputGenerator(List jApiClasses, Options options, HtmlOutputGeneratorOptions htmlOutputGeneratorOptions) { + super(options, jApiClasses); + this.htmlOutputGeneratorOptions = htmlOutputGeneratorOptions; + } + + @Override + public HtmlOutput generate() { + OutputFilter outputFilter = new OutputFilter(options); + outputFilter.filter(jApiClasses); + StringBuilder sb = new StringBuilder(); + sb.append("\n"); + sb.append("\n"); + sb.append("").append(getTitle()).append("\n"); + sb.append("\n"); + sb.append("\n"); + sb.append("\n"); + sb.append("").append(getTitle()).append("\n"); + sb.append("
"); + metaInformation(sb); + warningMissingClasses(sb); + toc(sb); + explanations(sb); + classes(sb); + sb.append("\n"); + sb.append("\n"); + return new HtmlOutput(sb.toString()); + } + + private void classes(StringBuilder sb) { + sb.append(jApiClasses.stream() + .map(jApiClass -> loadAndFillTemplate("/html/class-entry.html", mapOf( + "fullyQualifiedName", jApiClass.getFullyQualifiedName(), + "outputChangeStatus", outputChangeStatus(jApiClass), + "javaObjectSerializationCompatibleClass", javaObjectSerializationCompatibleClass(jApiClass), + "javaObjectSerializationCompatible", javaObjectSerializationCompatible(jApiClass), + "modifiers", modifiers(jApiClass), + "classType", classType(jApiClass), + "compatibilityChanges", compatibilityChanges(jApiClass), + "classFileFormatVersion", classFileFormatVersion(jApiClass), + "genericTemplates", genericTemplates(jApiClass), + "superclass", superclass(jApiClass), + "interfaces", interfaces(jApiClass), + "serialVersionUid", serialVersionUid(jApiClass), + "fields", fields(jApiClass), + "constructors", constructors(jApiClass) + ))) + .collect(Collectors.joining())); + } + + private String constructors(JApiClass jApiClass) { + if (!jApiClass.getConstructors().isEmpty()) { + return loadAndFillTemplate("/html/constructors.html", mapOf( + "tbody", constructors(jApiClass.getConstructors()) + )); + } + return ""; + } + + private String constructors(List constructors) { + return constructors.stream() + .map(constructor -> "\n" + + "" + outputChangeStatus(constructor) + "\n" + + "" + modifiers(constructor) + "\n" + + "" + genericTemplates(constructor) + "\n" + + "" + constructor.getName() + "(" + parameters(constructor) + ")" + annotations(constructor.getAnnotations()) + "\n" + + "" + exceptions(constructor) + "\n" + + "" + compatibilityChanges(constructor) + "\n" + + "" + + loadAndFillTemplate("/html/line-numbers.html", mapOf( + "oldLineNumber", constructor.getOldLineNumberAsString(), + "newLineNumber", constructor.getNewLineNumberAsString())) + "\n" + + "\n") + .collect(Collectors.joining()); + } + + private String exceptions(JApiConstructor constructor) { + if (!constructor.getExceptions().isEmpty()) { + return loadAndFillTemplate("/html/exceptions.html", mapOf( + "tbody", exceptionsTBody(constructor.getExceptions()) + )); + } + return ""; + } + + private String exceptionsTBody(List exceptions) { + return exceptions.stream() + .map(exc -> "\n" + + "" + outputChangeStatus(exc) + "\n" + + "" + exc.getName() + "\n" + + "\n") + .collect(Collectors.joining()); + } + + private String parameters(JApiConstructor constructor) { + return constructor.getParameters().stream() + .map(parameter -> "" + + parameter.getType() + + genericParameterTypes(parameter) + + binaryAndSourceCompatibility(parameter) + + "") + .collect(Collectors.joining(", ")); + } + + private String fields(JApiClass jApiClass) { + if (!jApiClass.getFields().isEmpty()) { + return loadAndFillTemplate("/html/fields.html", mapOf( + "tbody", fields(jApiClass.getFields()) + )); + } + return ""; + } + + private String fields(List fields) { + return fields.stream() + .map(field -> "\n" + + "" + outputChangeStatus(field) + "\n" + + "" + modifiers(field) + "\n" + + "" + type(field) + "\n" + + "" + field.getName() + annotations(field.getAnnotations()) + "\n" + + "" + compatibilityChanges(field) + "\n" + + "\n") + .collect(Collectors.joining()); + } + + private String type(JApiField field) { + return "" + + typeValue(field) + + ""; + } + + private String typeValue(JApiField field) { + JApiType type = field.getType(); + switch (type.getChangeStatus()) { + case NEW: + case UNCHANGED: + return type.getNewValue() + genericParameterTypes(field); + case REMOVED: + return type.getOldValue() + genericParameterTypes(field); + case MODIFIED: + return type.getNewValue() + " (<- " + type.getOldValue() + genericParameterTypes(field); + } + return ""; + } + + private String annotations(List annotations) { + if (!annotations.isEmpty()) { + return loadAndFillTemplate("/html/annotations.html", mapOf( + "tbody", annotationsTBody(annotations) + )); + } + return ""; + } + + private String annotationsTBody(List annotations) { + return annotations.stream() + .map(annotation -> "\n" + + "" + outputChangeStatus(annotation) + "\n" + + "" + annotation.getFullyQualifiedName() + "\n" + + "" + elements(annotation) + "\n" + + "\n") + .collect(Collectors.joining()); + } + + private String elements(JApiAnnotation annotation) { + if (!annotation.getElements().isEmpty()) { + return loadAndFillTemplate("/html/annotation-elements.html", mapOf( + "tbody", annotationElements(annotation.getElements()) + )); + } else { + return "n.a."; + } + } + + private String annotationElements(List elements) { + return elements.stream() + .map(element -> "\n" + + "" + outputChangeStatus(element) + "\n" + + "" + element.getName() + "\n" + + "" + element.getOldElementValues().stream() + .map(this::valueToString) + .collect(Collectors.joining()) + + "\n" + + "" + element.getNewElementValues().stream() + .map(this::valueToString) + .collect(Collectors.joining()) + + "\n" + + "\n") + .collect(Collectors.joining()); + } + + private String valueToString(JApiAnnotationElementValue value) { + switch (value.getType()) { + case Annotation: + return "@" + value.getFullyQualifiedName() + "(" + values(value) + ")"; + case Array: + return "{" + values(value) + "}"; + case Enum: + return value.getFullyQualifiedName() + "." + value.getValue(); + default: + return String.valueOf(value); + } + } + + private String values(JApiAnnotationElementValue value) { + return value.getValues().stream() + .map(this::valueToString) + .collect(Collectors.joining()); + } + + private String serialVersionUid(JApiClass jApiClass) { + if (jApiClass.getSerialVersionUid().isSerializableOld() || jApiClass.getSerialVersionUid().isSerializableNew()) { + return loadAndFillTemplate("/html/serial-version-uid.html", mapOf( + "tbody", serialVersionUidTBody(jApiClass.getSerialVersionUid()) + )); + } + return ""; + } + + private String serialVersionUidTBody(JApiSerialVersionUid serialVersionUid) { + return "\n" + + "Old" + + "" + serialVersionUid.isSerializableOld() + "\n" + + "" + serialVersionUid.getSerialVersionUidDefaultOldAsString() + "\n" + + "" + serialVersionUid.getSerialVersionUidInClassOldAsString() + "\n" + + "\n" + + "\n" + + "New" + + "" + serialVersionUid.isSerializableNew() + "\n" + + "" + serialVersionUid.getSerialVersionUidDefaultNewAsString() + "\n" + + "" + serialVersionUid.getSerialVersionUidInClassNewAsString() + "\n" + + "\n"; + } + + private String interfaces(JApiClass jApiClass) { + if (!jApiClass.getInterfaces().isEmpty()) { + return loadAndFillTemplate("/html/interfaces.html", mapOf( + "tbody", interfacesTBody(jApiClass.getInterfaces()) + )); + } + return ""; + } + + private String interfacesTBody(List interfaces) { + return interfaces.stream() + .map(interfaze -> "\n" + + "" + outputChangeStatus(interfaze) + "\n" + + "" + interfaze.getFullyQualifiedName() + "\n" + + "" + compatibilityChanges(interfaze) + "\n" + + "\n") + .collect(Collectors.joining()); + } + + private String superclass(JApiClass jApiClass) { + JApiSuperclass superclass = jApiClass.getSuperclass(); + if ((superclass.getOldSuperclass().isPresent() || superclass.getNewSuperclass().isPresent()) && + ((superclass.getChangeStatus() == JApiChangeStatus.NEW && !superclass.getSuperclassNew().equalsIgnoreCase("java.lang.Object")) || + (superclass.getChangeStatus() == JApiChangeStatus.REMOVED && !superclass.getSuperclassOld().equalsIgnoreCase("java.lang.Object")) || + (superclass.getChangeStatus() == JApiChangeStatus.MODIFIED) || + (superclass.getChangeStatus() == JApiChangeStatus.UNCHANGED && !superclass.getSuperclassOld().equalsIgnoreCase("java.lang.Object")) + ) + ) { + return loadAndFillTemplate("/html/superclass.html", mapOf( + "tbody", superclassTBody(jApiClass.getSuperclass()) + )); + } + return ""; + } + + private String superclassTBody(JApiSuperclass superclass) { + return "\n" + + "" + outputChangeStatus(superclass) + "\n" + + "" + superclassName(superclass) + "\n" + + "" + compatibilityChanges(superclass) + "\n" + + "\n"; + } + + private String superclassName(JApiSuperclass superclass) { + switch (superclass.getChangeStatus()) { + case NEW: + case UNCHANGED: + return superclass.getSuperclassNew(); + case REMOVED: + return superclass.getSuperclassOld(); + case MODIFIED: + return superclass.getSuperclassNew() + "(<- " + superclass.getSuperclassOld() + ")"; + } + return ""; + } + + private String genericTemplates(JApiHasGenericTemplates jApiHasGenericTemplates) { + List genericTemplates = jApiHasGenericTemplates.getGenericTemplates(); + if (!genericTemplates.isEmpty()) { + return "Generic Templates:\n" + + loadAndFillTemplate("/html/generic-templates.html", mapOf( + "tbody", genericTemplatesTBody(genericTemplates) + )); + } + return ""; + } + + private String genericTemplatesTBody(List genericTemplates) { + return genericTemplates.stream() + .map(jApiGenericTemplate -> "\n" + + "" + outputChangeStatus(jApiGenericTemplate) + "\n" + + "" + jApiGenericTemplate.getName() + "\n" + + "" + jApiGenericTemplate.getOldType() + interfaceTypes(jApiGenericTemplate.getOldInterfaceTypes()) + "\n" + + "" + jApiGenericTemplate.getNewType() + "\n" + + "" + genericParameterTypes(jApiGenericTemplate) + "\n" + + "\n") + .collect(Collectors.joining()); + } + + private String interfaceTypes(List interfaceTypes) { + if (!interfaceTypes.isEmpty()) { + return interfaceTypes.stream() + .map(interfaceType -> "& " + interfaceType.getType() + genericParameterTypesRecursive(interfaceType)) + .collect(Collectors.joining()); + } + return ""; + } + + private String genericParameterTypesRecursive(JApiGenericType jApiGenericType) { + if (!jApiGenericType.getGenericTypes().isEmpty()) { + return "<" + jApiGenericType.getGenericTypes().stream() + .map(jApiGenericType1 -> genericParameterWithWildcard(jApiGenericType1) + genericParameterTypesRecursive(jApiGenericType1)) + .collect(Collectors.joining(",")) + + ">"; + } + return ""; + } + + private String genericParameterWithWildcard(JApiGenericType jApiGenericType1) { + switch (jApiGenericType1.getGenericWildCard()) { + case NONE: + return jApiGenericType1.getType(); + case EXTENDS: + return "? extends " + jApiGenericType1.getType(); + case SUPER: + return "? super " + jApiGenericType1.getType(); + case UNBOUNDED: + return "? " + jApiGenericType1.getType(); + } + return ""; + } + + private String genericParameterTypes(JApiHasGenericTypes jApiHasGenericTypes) { + if (!jApiHasGenericTypes.getNewGenericTypes().isEmpty() || !jApiHasGenericTypes.getOldGenericTypes().isEmpty()) { + return "
" + + "<..>" + + "
" + + "" + + genericTypes("New", jApiHasGenericTypes.getNewGenericTypes()) + + genericTypes("Old", jApiHasGenericTypes.getOldGenericTypes()) + + "
" + + "
" + + "
"; + } + return ""; + } + + private String genericTypes(String header, List genericTypes) { + if (!genericTypes.isEmpty()) { + return "" + + "" + header + ":" + + genericTypes.stream() + .map(genericType -> "" + + genericParameterWithWildcard(genericType) + + genericParameterTypesRecursive(genericType) + + "") + .collect(Collectors.joining()) + + ""; + } + return ""; + } + + private String classFileFormatVersion(JApiClass jApiClass) { + if (jApiClass.getClassFileFormatVersion().getChangeStatus() == JApiChangeStatus.MODIFIED) { + return loadAndFillTemplate("/html/class-file-format-version.html", mapOf( + "tbody", classFileFormatVersionTBody(jApiClass) + )); + } + return ""; + } + + private String classFileFormatVersionTBody(JApiClass jApiClass) { + JApiClassFileFormatVersion classFileFormatVersion = jApiClass.getClassFileFormatVersion(); + return "\n" + + "" + outputChangeStatus(classFileFormatVersion) + "\n" + + "" + classFileFormatVersionString(classFileFormatVersion.getMajorVersionOld(), classFileFormatVersion.getMinorVersionOld()) + "\n" + + "" + classFileFormatVersionString(classFileFormatVersion.getMajorVersionNew(), classFileFormatVersion.getMinorVersionNew()) + "\n" + + "\n"; + } + + private String classFileFormatVersionString(int majorVersion, int minorVersion) { + if (majorVersion >= 0 && minorVersion >= 0) { + return majorVersion + "." + minorVersion; + } + return "n.a."; + } + + private String compatibilityChanges(JApiCompatibility jApiClass) { + if (!jApiClass.getCompatibilityChanges().isEmpty()) { + return loadAndFillTemplate("/html/compatibility-changes.html", mapOf( + "tbody", jApiClass.getCompatibilityChanges().stream() + .map(this::compatibilityChange) + .collect(Collectors.joining()) + )); + } + return ""; + } + + private String compatibilityChange(JApiCompatibilityChange jApiCompatibilityChange) { + return "" + jApiCompatibilityChange.getType() + "\n"; + } + + private String classType(JApiClass jApiClass) { + return "" + + classTypeValue(jApiClass.getClassType()) + + "\n"; + } + + private String classTypeValue(JApiClassType classType) { + if (classType.getChangeStatus() == JApiChangeStatus.MODIFIED) { + return classType.getNewType().toLowerCase() + " (<- " + classType.getOldType().toLowerCase() + ")"; + } else if (classType.getChangeStatus() == JApiChangeStatus.NEW || classType.getChangeStatus() == JApiChangeStatus.UNCHANGED) { + return classType.getNewType().toLowerCase(); + } else if (classType.getChangeStatus() == JApiChangeStatus.REMOVED) { + return classType.getOldType().toLowerCase(); + } + return ""; + } + + private String modifiers(JApiHasModifiers jApiHasModifiers) { + return jApiHasModifiers.getModifiers().stream() + .map(jApiModifier -> { + String modifier = modifier(jApiModifier); + if (!modifier.trim().isEmpty()) { + return "" + + modifier + + "\n"; + } + return ""; + }) + .collect(Collectors.joining()); + } + + private String modifier(JApiModifier>> jApiModifier) { + if (jApiModifier.getChangeStatus() == JApiChangeStatus.MODIFIED) { + return jApiModifier.getValueNew() + " (<- " + jApiModifier.getValueOld() + ") "; + } else if (jApiModifier.getChangeStatus() == JApiChangeStatus.UNCHANGED) { + return jApiModifier.getValueNew().toLowerCase().startsWith("non") || jApiModifier.getValueNew().equalsIgnoreCase("package_protected") ? "" : jApiModifier.getValueNew().toLowerCase(); + } else if (jApiModifier.getChangeStatus() == JApiChangeStatus.NEW) { + return jApiModifier.getValueNew().toLowerCase().startsWith("non") || jApiModifier.getValueNew().equalsIgnoreCase("package_protected") ? "" : jApiModifier.getValueNew().toLowerCase(); + } else if (jApiModifier.getChangeStatus() == JApiChangeStatus.REMOVED) { + return jApiModifier.getValueOld().toLowerCase().startsWith("non") || jApiModifier.getValueOld().equalsIgnoreCase("package_protected") ? "" : jApiModifier.getValueOld().toLowerCase(); + } + return ""; + } + + private String javaObjectSerializationCompatibleClass(JApiClass jApiClass) { + if (jApiClass.getJavaObjectSerializationCompatible() == JApiJavaObjectSerializationCompatibility.JApiJavaObjectSerializationChangeStatus.NOT_SERIALIZABLE) { + return ""; + } else if (jApiClass.getJavaObjectSerializationCompatible() == JApiJavaObjectSerializationCompatibility.JApiJavaObjectSerializationChangeStatus.SERIALIZABLE_COMPATIBLE) { + return "new"; + } else { + return "removed"; + } + } + + private String javaObjectSerializationCompatible(JApiClass jApiClass) { + if (jApiClass.getJavaObjectSerializationCompatible() == JApiJavaObjectSerializationCompatibility.JApiJavaObjectSerializationChangeStatus.NOT_SERIALIZABLE) { + return ""; + } else if (jApiClass.getJavaObjectSerializationCompatible() == JApiJavaObjectSerializationCompatibility.JApiJavaObjectSerializationChangeStatus.SERIALIZABLE_COMPATIBLE) { + return " (Serializable compatible) "; + } else { + return " (Serializable incompatible(!): " + jApiClass.getJavaObjectSerializationCompatibleAsString() + ") "; + } + } + + private void explanations(StringBuilder sb) { + sb.append("
\n" + + "Binary incompatible changes are marked with (!) while source incompatible changes are marked with (*).\n" + + "
\n"); + } + + private void toc(StringBuilder sb) { + if (!jApiClasses.isEmpty()) { + sb.append("
    \n"); + sb.append("
  • \n"); + sb.append("Classes\n"); + sb.append("
  • \n"); + sb.append("
\n"); + sb.append(loadAndFillTemplate("/html/toc.html", mapOf( + "tbody", tocEntries() + ))); + } + } + + private String tocEntries() { + return jApiClasses.stream() + .map(jApiClass -> loadAndFillTemplate("/html/toc-entry.html", mapOf( + "outputChangeStatus", outputChangeStatus(jApiClass), + "fullyQualifiedName", jApiClass.getFullyQualifiedName() + ))) + .collect(Collectors.joining()); + } + + private String outputChangeStatus(JApiHasChangeStatus jApiHasChangeStatus) { + return "" + + jApiHasChangeStatus.getChangeStatus().name() + + (jApiHasChangeStatus instanceof JApiCompatibility ? binaryAndSourceCompatibility((JApiCompatibility) jApiHasChangeStatus) : "") + + ""; + } + + private String binaryAndSourceCompatibility(JApiCompatibility jApiCompatibility) { + if (!jApiCompatibility.isBinaryCompatible()) { + return " (!)"; + } else if (jApiCompatibility.isBinaryCompatible() && !jApiCompatibility.isSourceCompatible()) { + return " (*)"; + } + return ""; + } + + private void warningMissingClasses(StringBuilder sb) { + if (options.getIgnoreMissingClasses().isIgnoreAllMissingClasses()) { + sb.append("
\n" + + "\n" + + "WARNING: You are using the option '--ignore-missing-classes', i.e. superclasses and\n" + + "interfaces that could not be found on the classpath are ignored. Hence changes\n" + + "caused by these superclasses and interfaces are not reflected in the output.\n" + + "\n" + + "
" + ); + } + } + + private String getStyle() { + String styleSheet; + if (options.getHtmlStylesheet().isPresent()) { + try { + InputStream inputStream = new FileInputStream(options.getHtmlStylesheet().get()); + styleSheet = Streams.asString(inputStream); + } catch (FileNotFoundException e) { + throw new JApiCmpException(JApiCmpException.Reason.IoException, "Failed to load stylesheet: " + e.getMessage(), e); + } + } else { + styleSheet = loadTemplate("/style.css"); + } + return styleSheet; + } + + private void metaInformation(StringBuilder sb) { + sb.append(loadAndFillTemplate("/html/meta-information.html", mapOf( + "oldJar", options.joinOldArchives(), + "newJar", options.joinNewArchives(), + "newJar", options.joinNewArchives(), + "creationTimestamp", DATE_FORMAT.format(new Date()), + "accessModifier", options.getAccessModifier().name(), + "onlyModifications", String.valueOf(options.isOutputOnlyModifications()), + "onlyBinaryIncompatibleModifications", String.valueOf(options.isOutputOnlyBinaryIncompatibleModifications()), + "ignoreMissingClasses", String.valueOf(options.getIgnoreMissingClasses().isIgnoreAllMissingClasses()), + "packagesInclude", filtersAsString(options.getIncludes(), true), + "packagesExclude", filtersAsString(options.getExcludes(), false), + "semanticVersioning", htmlOutputGeneratorOptions.getSemanticVersioningInformation() + ))).append("\n"); + } + + private Map mapOf(String... args) { + Map map = new HashMap<>(); + int count = args.length / 2; + for (int i = 0; i < count; i++) { + map.put(args[i * 2], args[(i * 2) + 1]); + } + return map; + } + + private String loadAndFillTemplate(String path, Map params) { + String template = loadTemplate(path); + for (Map.Entry entry : params.entrySet()) { + template = template.replace("${" + entry.getKey() + "}", entry.getValue()); + } + return template; + } + + private String loadTemplate(String path) { + InputStream resourceAsStream = HtmlOutputGenerator.class.getResourceAsStream(path); + if (resourceAsStream == null) { + throw new JApiCmpException(JApiCmpException.Reason.ResourceNotFound, "Failed to load: " + path); + } + return Streams.asString(resourceAsStream); + } + + private String getTitle() { + if (this.htmlOutputGeneratorOptions.getTitle().isPresent()) { + return this.htmlOutputGeneratorOptions.getTitle().get(); + } + return "japicmp-Report"; + } +} diff --git a/japicmp/src/main/java/japicmp/output/html/HtmlOutputGeneratorOptions.java b/japicmp/src/main/java/japicmp/output/html/HtmlOutputGeneratorOptions.java new file mode 100644 index 000000000..2f8d96afa --- /dev/null +++ b/japicmp/src/main/java/japicmp/output/html/HtmlOutputGeneratorOptions.java @@ -0,0 +1,24 @@ +package japicmp.output.html; + +import japicmp.util.Optional; + +public class HtmlOutputGeneratorOptions { + private Optional title = Optional.absent(); + private String semanticVersioningInformation = "n.a."; + + public Optional getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = Optional.fromNullable(title); + } + + public String getSemanticVersioningInformation() { + return semanticVersioningInformation; + } + + public void setSemanticVersioningInformation(String semanticVersioningInformation) { + this.semanticVersioningInformation = semanticVersioningInformation; + } +} diff --git a/japicmp/src/main/java/japicmp/output/xml/XmlOutputGenerator.java b/japicmp/src/main/java/japicmp/output/xml/XmlOutputGenerator.java index 0024d9502..5a765d4d6 100644 --- a/japicmp/src/main/java/japicmp/output/xml/XmlOutputGenerator.java +++ b/japicmp/src/main/java/japicmp/output/xml/XmlOutputGenerator.java @@ -1,38 +1,25 @@ package japicmp.output.xml; -import com.google.common.base.Joiner; -import japicmp.util.Optional; import japicmp.config.Options; import japicmp.exception.JApiCmpException; import japicmp.exception.JApiCmpException.Reason; -import japicmp.filter.Filter; import japicmp.model.JApiClass; import japicmp.output.OutputFilter; import japicmp.output.OutputGenerator; import japicmp.output.extapi.jpa.JpaAnalyzer; import japicmp.output.extapi.jpa.model.JpaTable; import japicmp.output.xml.model.JApiCmpXmlRoot; +import japicmp.util.Optional; import japicmp.util.Streams; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.SchemaOutputResolver; -import javax.xml.transform.Result; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerConfigurationException; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; +import javax.xml.transform.*; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.StringReader; +import java.io.*; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Paths; @@ -44,6 +31,8 @@ import java.util.logging.Logger; import java.util.regex.Pattern; +import static japicmp.util.StringHelper.filtersAsString; + public class XmlOutputGenerator extends OutputGenerator { private static final String XSD_FILENAME = "japicmp.xsd"; private static final String XML_SCHEMA = XSD_FILENAME; @@ -240,17 +229,5 @@ private String regExAsString(List ignoreMissingClassRegularExpression) return sb.toString(); } - private String filtersAsString(List filters, boolean include) { - String join; - if (filters.size() == 0) { - if (include) { - join = "all"; - } else { - join = "n.a."; - } - } else { - join = Joiner.on(";").skipNulls().join(filters); - } - return join; - } + } diff --git a/japicmp/src/main/java/japicmp/util/StringHelper.java b/japicmp/src/main/java/japicmp/util/StringHelper.java new file mode 100644 index 000000000..e14fd0580 --- /dev/null +++ b/japicmp/src/main/java/japicmp/util/StringHelper.java @@ -0,0 +1,27 @@ +package japicmp.util; + +import com.google.common.base.Joiner; +import japicmp.filter.Filter; + +import java.util.List; + +public class StringHelper { + + private StringHelper() { + // private constructor + } + + public static String filtersAsString(List filters, boolean include) { + String join; + if (filters.isEmpty()) { + if (include) { + join = "all"; + } else { + join = "n.a."; + } + } else { + join = Joiner.on(";").skipNulls().join(filters); + } + return join; + } +} diff --git a/japicmp/src/main/resources/html/annotation-elements.html b/japicmp/src/main/resources/html/annotation-elements.html new file mode 100644 index 000000000..4f5e0d11c --- /dev/null +++ b/japicmp/src/main/resources/html/annotation-elements.html @@ -0,0 +1,13 @@ + + + + + + + + + + + ${tbody} + +
Status:Name:Old element values:New element values:
diff --git a/japicmp/src/main/resources/html/annotations.html b/japicmp/src/main/resources/html/annotations.html new file mode 100644 index 000000000..fb3744d76 --- /dev/null +++ b/japicmp/src/main/resources/html/annotations.html @@ -0,0 +1,15 @@ +
+ Annotations: + + + + + + + + + + ${tbody} + +
Status:Fully Qualified Name:Elements:
+
diff --git a/japicmp/src/main/resources/html/class-entry.html b/japicmp/src/main/resources/html/class-entry.html new file mode 100644 index 000000000..d32806fe6 --- /dev/null +++ b/japicmp/src/main/resources/html/class-entry.html @@ -0,0 +1,57 @@ +
+
+
+ + + ${outputChangeStatus} + ${javaObjectSerializationCompatible} + ${modifiers} + ${classType} ${fullyQualifiedName} + + top +
+ ${compatibilityChanges} + ${classFileFormatVersion} +
+ ${genericTemplates} +
+
+ ${superclass} +
+
+ ${interfaces} +
+ ${serialVersionUid} +
+ ${fields} +
+
+ ${constructors} +
+
+ + Methods: + + + + + + + + + + + + + + + + + + +
StatusModifierGeneric TemplatesTypeMethodExceptionsCompatibility Changes:Line Number
+
+
+ +
+
diff --git a/japicmp/src/main/resources/html/class-file-format-version.html b/japicmp/src/main/resources/html/class-file-format-version.html new file mode 100644 index 000000000..6a5e66542 --- /dev/null +++ b/japicmp/src/main/resources/html/class-file-format-version.html @@ -0,0 +1,15 @@ +
+ class File Format Version: + + + + + + + + + + ${tbody} + +
StatusOld VersionNew Version
+
diff --git a/japicmp/src/main/resources/html/compatibility-changes.html b/japicmp/src/main/resources/html/compatibility-changes.html new file mode 100644 index 000000000..3b3fe5546 --- /dev/null +++ b/japicmp/src/main/resources/html/compatibility-changes.html @@ -0,0 +1,13 @@ +
+ Compatibility Changes: + + + + + + + + ${tbody} + +
Change
+
diff --git a/japicmp/src/main/resources/html/constructors.html b/japicmp/src/main/resources/html/constructors.html new file mode 100644 index 000000000..5d5c547a0 --- /dev/null +++ b/japicmp/src/main/resources/html/constructors.html @@ -0,0 +1,17 @@ +Constructors: + + + + + + + + + + + + + + ${tbody} + +
StatusModifierGeneric TemplatesConstructorExceptionsCompatibility Changes:Line Number
diff --git a/japicmp/src/main/resources/html/exceptions.html b/japicmp/src/main/resources/html/exceptions.html new file mode 100644 index 000000000..c5718a54c --- /dev/null +++ b/japicmp/src/main/resources/html/exceptions.html @@ -0,0 +1,11 @@ + + + + + + + + + ${tbody} + +
Status:Name:
diff --git a/japicmp/src/main/resources/html/fields.html b/japicmp/src/main/resources/html/fields.html new file mode 100644 index 000000000..f012bbc5a --- /dev/null +++ b/japicmp/src/main/resources/html/fields.html @@ -0,0 +1,15 @@ +Fields: + + + + + + + + + + + + ${tbody} + +
StatusModifierTypeFieldCompatibility Changes:
diff --git a/japicmp/src/main/resources/html/generic-templates.html b/japicmp/src/main/resources/html/generic-templates.html new file mode 100644 index 000000000..41ac159f3 --- /dev/null +++ b/japicmp/src/main/resources/html/generic-templates.html @@ -0,0 +1,14 @@ + + + + + + + + + + + + ${tbody} + +
Change StatusNameOld TypeNew TypeGenerics
diff --git a/japicmp/src/main/resources/html/interfaces.html b/japicmp/src/main/resources/html/interfaces.html new file mode 100644 index 000000000..210336b65 --- /dev/null +++ b/japicmp/src/main/resources/html/interfaces.html @@ -0,0 +1,13 @@ +Interfaces: + + + + + + + + + + ${tbody} + +
StatusInterfaceCompatibility Changes
diff --git a/japicmp/src/main/resources/html/line-numbers.html b/japicmp/src/main/resources/html/line-numbers.html new file mode 100644 index 000000000..41fd971b7 --- /dev/null +++ b/japicmp/src/main/resources/html/line-numbers.html @@ -0,0 +1,18 @@ + + + + + + + + + + + + + +
Old fileNew file
+ ${oldLineNumber} + + ${newLineNumber} +
diff --git a/japicmp/src/main/resources/html/meta-information.html b/japicmp/src/main/resources/html/meta-information.html new file mode 100644 index 000000000..d1cc506a3 --- /dev/null +++ b/japicmp/src/main/resources/html/meta-information.html @@ -0,0 +1,64 @@ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Old: + ${oldJar} +
New: + ${newJar} +
Created: + ${creationTimestamp} +
Access modifier filter: + ${accessModifier} +
Only modifications: + ${onlyModifications} +
Only binary incompatible modifications: + ${onlyBinaryIncompatibleModifications} +
Ignore missing classes: + ${ignoreMissingClasses} +
Includes: + ${packagesInclude} +
Excludes: + ${packagesExclude} +
Semantic Versioning: + ${semanticVersioning} +
+
diff --git a/japicmp/src/main/resources/html/serial-version-uid.html b/japicmp/src/main/resources/html/serial-version-uid.html new file mode 100644 index 000000000..14d0c1022 --- /dev/null +++ b/japicmp/src/main/resources/html/serial-version-uid.html @@ -0,0 +1,15 @@ +
+ + + + + + + + + + + ${tbody} + +
Serializabledefault serialVersionUIDserialVersionUID in class
+
diff --git a/japicmp/src/main/resources/html/superclass.html b/japicmp/src/main/resources/html/superclass.html new file mode 100644 index 000000000..c76087faa --- /dev/null +++ b/japicmp/src/main/resources/html/superclass.html @@ -0,0 +1,13 @@ +Superclass: + + + + + + + + + + ${tbody} + +
StatusSuperclassCompatibility Changes
diff --git a/japicmp/src/main/resources/html/toc-entry.html b/japicmp/src/main/resources/html/toc-entry.html new file mode 100644 index 000000000..25188ef6e --- /dev/null +++ b/japicmp/src/main/resources/html/toc-entry.html @@ -0,0 +1,10 @@ + + + ${outputChangeStatus} + + + + ${fullyQualifiedName} + + + diff --git a/japicmp/src/main/resources/html/toc.html b/japicmp/src/main/resources/html/toc.html new file mode 100644 index 000000000..68b3e24b1 --- /dev/null +++ b/japicmp/src/main/resources/html/toc.html @@ -0,0 +1,14 @@ +
+ Classes: + + + + + + + + + ${tbody} + +
StatusFully Qualified Name
+
diff --git a/japicmp/src/test/java/japicmp/output/html/HtmlOutputGeneratorTest.java b/japicmp/src/test/java/japicmp/output/html/HtmlOutputGeneratorTest.java new file mode 100644 index 000000000..3267313e7 --- /dev/null +++ b/japicmp/src/test/java/japicmp/output/html/HtmlOutputGeneratorTest.java @@ -0,0 +1,63 @@ +package japicmp.output.html; + +import japicmp.cmp.ClassesHelper; +import japicmp.cmp.JarArchiveComparatorOptions; +import japicmp.config.Options; +import japicmp.model.JApiClass; +import japicmp.util.*; +import javassist.CannotCompileException; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.NotFoundException; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.junit.Test; + +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.StringContains.containsString; + +public class HtmlOutputGeneratorTest { + + @Test + public void testHtmlReport() throws Exception { + JarArchiveComparatorOptions options = new JarArchiveComparatorOptions(); + options.setIncludeSynthetic(true); + List jApiClasses = ClassesHelper.compareClasses(options, new ClassesHelper.ClassesGenerator() { + @Override + public List createOldClasses(ClassPool classPool) throws CannotCompileException { + CtClass ctClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool); + CtMethodBuilder.create().name("toBeRemoved").returnType(CtClass.booleanType).addToClass(ctClass); + return Collections.singletonList(ctClass); + } + + @Override + public List createNewClasses(ClassPool classPool) throws CannotCompileException, NotFoundException { + CtClass newInterface = CtInterfaceBuilder.create().name("NewInterface").addToClassPool(classPool); + CtClass superclass = CtClassBuilder.create().name("japicmp.Superclass").addToClassPool(classPool); + CtClass ctClass = CtClassBuilder.create().name("japicmp.Test").withSuperclass(superclass).implementsInterface(newInterface).addToClassPool(classPool); + CtMethodBuilder.create().name("newMethod").returnType(CtClass.booleanType).addToClass(ctClass); + CtFieldBuilder.create().type(CtClass.booleanType).name("bField").addToClass(ctClass); + CtConstructorBuilder.create().publicAccess().parameters(new CtClass[] {CtClass.intType, CtClass.booleanType}).exceptions(new CtClass[] {classPool.get("java.lang.Exception")}).addToClass(ctClass); + return Arrays.asList(ctClass, superclass); + } + }); + Options reportOptions = Options.newDefault(); + reportOptions.setIgnoreMissingClasses(true); + HtmlOutputGenerator generator = new HtmlOutputGenerator(jApiClasses, reportOptions, new HtmlOutputGeneratorOptions()); + + HtmlOutput htmlOutput = generator.generate(); + + Files.write(Paths.get(System.getProperty("user.dir"), "target", "report.html"), htmlOutput.getHtml().getBytes(StandardCharsets.UTF_8)); + Document document = Jsoup.parse(htmlOutput.getHtml()); + assertThat(document.select("#meta-accessmodifier-value").text(), is("PROTECTED")); + assertThat(document.select("#warning-missingclasses").text(), containsString("WARNING")); + } +} diff --git a/pom.xml b/pom.xml index 06e3a5841..5ce02ee3c 100644 --- a/pom.xml +++ b/pom.xml @@ -171,6 +171,12 @@ 4.3.1 test + + org.jsoup + jsoup + 1.15.3 + test + From d6c22ee407b44197b9c69c4fa4c055184b03fe99 Mon Sep 17 00:00:00 2001 From: Martin Mois Date: Mon, 4 Mar 2024 22:51:33 +0100 Subject: [PATCH 2/7] finished HtmlOutputGenerator --- japicmp/src/main/java/japicmp/JApiCmp.java | 2 +- .../src/main/java/japicmp/cli/JApiCli.java | 24 ++++- .../output/html/HtmlOutputGenerator.java | 101 ++++++++++++------ .../output/xml/XmlOutputGenerator.java | 91 ++-------------- .../src/main/resources/html/class-entry.html | 27 +---- japicmp/src/main/resources/html/methods.html | 18 ++++ .../output/html/HtmlOutputGeneratorTest.java | 40 ++++++- 7 files changed, 157 insertions(+), 146 deletions(-) create mode 100644 japicmp/src/main/resources/html/methods.html diff --git a/japicmp/src/main/java/japicmp/JApiCmp.java b/japicmp/src/main/java/japicmp/JApiCmp.java index 7e4e16bd0..3b3788d71 100644 --- a/japicmp/src/main/java/japicmp/JApiCmp.java +++ b/japicmp/src/main/java/japicmp/JApiCmp.java @@ -42,7 +42,7 @@ public static void main(String[] args) { } } catch (Exception e) { LOGGER.log(Level.FINE, CAUGHT_EXCEPTION + e.getLocalizedMessage(), e); - System.err.println(String.format("Execution of %s failed: %s", JApiCmp.class.getSimpleName(), e.getMessage())); + System.err.printf("Execution of %s failed: %s%n", JApiCmp.class.getSimpleName(), e.getMessage()); e.printStackTrace(); systemExit.exit(1); } diff --git a/japicmp/src/main/java/japicmp/cli/JApiCli.java b/japicmp/src/main/java/japicmp/cli/JApiCli.java index 1e78f1e0b..9ebf17816 100644 --- a/japicmp/src/main/java/japicmp/cli/JApiCli.java +++ b/japicmp/src/main/java/japicmp/cli/JApiCli.java @@ -5,6 +5,9 @@ import japicmp.config.Options; import japicmp.exception.JApiCmpException; import japicmp.model.JApiClass; +import japicmp.output.html.HtmlOutput; +import japicmp.output.html.HtmlOutputGenerator; +import japicmp.output.html.HtmlOutputGeneratorOptions; import japicmp.output.incompatible.IncompatibleErrorOutput; import japicmp.output.semver.SemverOut; import japicmp.output.stdout.StdoutOutputGenerator; @@ -12,6 +15,10 @@ import japicmp.output.xml.XmlOutputGenerator; import japicmp.output.xml.XmlOutputGeneratorOptions; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.List; public class JApiCli { @@ -39,8 +46,8 @@ private void generateOutput(Options options, List jApiClasses, JarArc System.out.println(output); return; } - if (options.getXmlOutputFile().isPresent() || options.getHtmlOutputFile().isPresent()) { - SemverOut semverOut = new SemverOut(options, jApiClasses); + SemverOut semverOut = new SemverOut(options, jApiClasses); + if (options.getXmlOutputFile().isPresent()) { XmlOutputGeneratorOptions xmlOutputGeneratorOptions = new XmlOutputGeneratorOptions(); xmlOutputGeneratorOptions.setCreateSchemaFile(true); xmlOutputGeneratorOptions.setSemanticVersioningInformation(semverOut.generate()); @@ -48,9 +55,20 @@ private void generateOutput(Options options, List jApiClasses, JarArc 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); + throw new JApiCmpException(JApiCmpException.Reason.IoException, "Could not write XML file: " + e.getMessage(), e); } } + if (options.getHtmlOutputFile().isPresent()) { + HtmlOutputGeneratorOptions htmlOutputGeneratorOptions = new HtmlOutputGeneratorOptions(); + htmlOutputGeneratorOptions.setSemanticVersioningInformation(semverOut.generate()); + HtmlOutputGenerator outputGenerator = new HtmlOutputGenerator(jApiClasses, options, htmlOutputGeneratorOptions); + HtmlOutput htmlOutput = outputGenerator.generate(); + try { + Files.write(Paths.get(options.getHtmlOutputFile().get()), htmlOutput.getHtml().getBytes(StandardCharsets.UTF_8)); + } catch (IOException e) { + throw new JApiCmpException(JApiCmpException.Reason.IoException, "Could not write HTML file: " + e.getMessage(), e); + } + } StdoutOutputGenerator stdoutOutputGenerator = new StdoutOutputGenerator(options, jApiClasses); String output = stdoutOutputGenerator.generate(); System.out.println(output); diff --git a/japicmp/src/main/java/japicmp/output/html/HtmlOutputGenerator.java b/japicmp/src/main/java/japicmp/output/html/HtmlOutputGenerator.java index 6e17b71ce..9d91bdff2 100644 --- a/japicmp/src/main/java/japicmp/output/html/HtmlOutputGenerator.java +++ b/japicmp/src/main/java/japicmp/output/html/HtmlOutputGenerator.java @@ -11,10 +11,7 @@ import java.io.FileNotFoundException; import java.io.InputStream; import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Collectors; import static japicmp.util.StringHelper.filtersAsString; @@ -57,22 +54,70 @@ private void classes(StringBuilder sb) { .map(jApiClass -> loadAndFillTemplate("/html/class-entry.html", mapOf( "fullyQualifiedName", jApiClass.getFullyQualifiedName(), "outputChangeStatus", outputChangeStatus(jApiClass), - "javaObjectSerializationCompatibleClass", javaObjectSerializationCompatibleClass(jApiClass), "javaObjectSerializationCompatible", javaObjectSerializationCompatible(jApiClass), "modifiers", modifiers(jApiClass), "classType", classType(jApiClass), - "compatibilityChanges", compatibilityChanges(jApiClass), + "compatibilityChanges", compatibilityChanges(jApiClass, false), "classFileFormatVersion", classFileFormatVersion(jApiClass), "genericTemplates", genericTemplates(jApiClass), "superclass", superclass(jApiClass), "interfaces", interfaces(jApiClass), "serialVersionUid", serialVersionUid(jApiClass), "fields", fields(jApiClass), - "constructors", constructors(jApiClass) + "constructors", constructors(jApiClass), + "methods", methods(jApiClass), + "annotations", annotations(jApiClass.getAnnotations()) ))) .collect(Collectors.joining())); } + private String methods(JApiClass jApiClass) { + if (!jApiClass.getMethods().isEmpty()) { + return loadAndFillTemplate("/html/methods.html", mapOf( + "tbody", methodsTBody(jApiClass.getMethods()) + )); + } + return ""; + } + + private String methodsTBody(List methods) { + return methods.stream() + .sorted(Comparator.comparing(JApiMethod::getName)) + .map(method -> "\n" + + "" + outputChangeStatus(method) + "\n" + + "" + modifiers(method) + "\n" + + "" + genericTemplates(method) + "\n" + + "" + returnType(method) + "\n" + + "" + method.getName() + "(" + parameters(method) + ")" + annotations(method.getAnnotations()) + "\n" + + "" + exceptions(method) + "\n" + + "" + compatibilityChanges(method, true) + "\n" + + "" + + loadAndFillTemplate("/html/line-numbers.html", mapOf( + "oldLineNumber", method.getOldLineNumberAsString(), + "newLineNumber", method.getNewLineNumberAsString())) + "\n" + + "\n") + .collect(Collectors.joining()); + } + + private String returnType(JApiMethod method) { + return "" + + returnTypeValue(method.getReturnType()) + + ""; + } + + private String returnTypeValue(JApiReturnType returnType) { + switch (returnType.getChangeStatus()) { + case NEW: + case UNCHANGED: + return returnType.getNewReturnType() + genericParameterTypes(returnType); + case REMOVED: + return returnType.getOldReturnType() + genericParameterTypes(returnType); + case MODIFIED: + return returnType.getNewReturnType() + " (<- " + returnType.getOldReturnType() + genericParameterTypes(returnType); + } + return ""; + } + private String constructors(JApiClass jApiClass) { if (!jApiClass.getConstructors().isEmpty()) { return loadAndFillTemplate("/html/constructors.html", mapOf( @@ -90,7 +135,7 @@ private String constructors(List constructors) { "" + genericTemplates(constructor) + "\n" + "" + constructor.getName() + "(" + parameters(constructor) + ")" + annotations(constructor.getAnnotations()) + "\n" + "" + exceptions(constructor) + "\n" + - "" + compatibilityChanges(constructor) + "\n" + + "" + compatibilityChanges(constructor, true) + "\n" + "" + loadAndFillTemplate("/html/line-numbers.html", mapOf( "oldLineNumber", constructor.getOldLineNumberAsString(), @@ -99,10 +144,10 @@ private String constructors(List constructors) { .collect(Collectors.joining()); } - private String exceptions(JApiConstructor constructor) { - if (!constructor.getExceptions().isEmpty()) { + private String exceptions(JApiBehavior jApiBehavior) { + if (!jApiBehavior.getExceptions().isEmpty()) { return loadAndFillTemplate("/html/exceptions.html", mapOf( - "tbody", exceptionsTBody(constructor.getExceptions()) + "tbody", exceptionsTBody(jApiBehavior.getExceptions()) )); } return ""; @@ -117,8 +162,8 @@ private String exceptionsTBody(List exceptions) { .collect(Collectors.joining()); } - private String parameters(JApiConstructor constructor) { - return constructor.getParameters().stream() + private String parameters(JApiBehavior jApiBehavior) { + return jApiBehavior.getParameters().stream() .map(parameter -> "" + parameter.getType() + genericParameterTypes(parameter) + @@ -138,12 +183,13 @@ private String fields(JApiClass jApiClass) { private String fields(List fields) { return fields.stream() + .sorted(Comparator.comparing(JApiField::getName)) .map(field -> "\n" + "" + outputChangeStatus(field) + "\n" + "" + modifiers(field) + "\n" + "" + type(field) + "\n" + "" + field.getName() + annotations(field.getAnnotations()) + "\n" + - "" + compatibilityChanges(field) + "\n" + + "" + compatibilityChanges(field, true) + "\n" + "\n") .collect(Collectors.joining()); } @@ -179,6 +225,7 @@ private String annotations(List annotations) { private String annotationsTBody(List annotations) { return annotations.stream() + .sorted(Comparator.comparing(JApiAnnotation::getFullyQualifiedName)) .map(annotation -> "\n" + "" + outputChangeStatus(annotation) + "\n" + "" + annotation.getFullyQualifiedName() + "\n" + @@ -204,11 +251,11 @@ private String annotationElements(List elements) { "" + element.getName() + "\n" + "" + element.getOldElementValues().stream() .map(this::valueToString) - .collect(Collectors.joining()) + + .collect(Collectors.joining(",")) + "\n" + "" + element.getNewElementValues().stream() .map(this::valueToString) - .collect(Collectors.joining()) + + .collect(Collectors.joining(",")) + "\n" + "\n") .collect(Collectors.joining()); @@ -271,7 +318,7 @@ private String interfacesTBody(List interfaces) { .map(interfaze -> "\n" + "" + outputChangeStatus(interfaze) + "\n" + "" + interfaze.getFullyQualifiedName() + "\n" + - "" + compatibilityChanges(interfaze) + "\n" + + "" + compatibilityChanges(interfaze, true) + "\n" + "\n") .collect(Collectors.joining()); } @@ -296,7 +343,7 @@ private String superclassTBody(JApiSuperclass superclass) { return "\n" + "" + outputChangeStatus(superclass) + "\n" + "" + superclassName(superclass) + "\n" + - "" + compatibilityChanges(superclass) + "\n" + + "" + compatibilityChanges(superclass, true) + "\n" + "\n"; } @@ -424,7 +471,7 @@ private String classFileFormatVersionString(int majorVersion, int minorVersion) return "n.a."; } - private String compatibilityChanges(JApiCompatibility jApiClass) { + private String compatibilityChanges(JApiCompatibility jApiClass, boolean withNA) { if (!jApiClass.getCompatibilityChanges().isEmpty()) { return loadAndFillTemplate("/html/compatibility-changes.html", mapOf( "tbody", jApiClass.getCompatibilityChanges().stream() @@ -432,7 +479,7 @@ private String compatibilityChanges(JApiCompatibility jApiClass) { .collect(Collectors.joining()) )); } - return ""; + return withNA ? "n.a." : ""; } private String compatibilityChange(JApiCompatibilityChange jApiCompatibilityChange) { @@ -483,23 +530,13 @@ private String modifier(JApiModifier>> jApiModi return ""; } - private String javaObjectSerializationCompatibleClass(JApiClass jApiClass) { - if (jApiClass.getJavaObjectSerializationCompatible() == JApiJavaObjectSerializationCompatibility.JApiJavaObjectSerializationChangeStatus.NOT_SERIALIZABLE) { - return ""; - } else if (jApiClass.getJavaObjectSerializationCompatible() == JApiJavaObjectSerializationCompatibility.JApiJavaObjectSerializationChangeStatus.SERIALIZABLE_COMPATIBLE) { - return "new"; - } else { - return "removed"; - } - } - private String javaObjectSerializationCompatible(JApiClass jApiClass) { if (jApiClass.getJavaObjectSerializationCompatible() == JApiJavaObjectSerializationCompatibility.JApiJavaObjectSerializationChangeStatus.NOT_SERIALIZABLE) { return ""; } else if (jApiClass.getJavaObjectSerializationCompatible() == JApiJavaObjectSerializationCompatibility.JApiJavaObjectSerializationChangeStatus.SERIALIZABLE_COMPATIBLE) { - return " (Serializable compatible) "; + return " (Serializable compatible) "; } else { - return " (Serializable incompatible(!): " + jApiClass.getJavaObjectSerializationCompatibleAsString() + ") "; + return " (Serializable incompatible(!): " + jApiClass.getJavaObjectSerializationCompatibleAsString() + ") "; } } diff --git a/japicmp/src/main/java/japicmp/output/xml/XmlOutputGenerator.java b/japicmp/src/main/java/japicmp/output/xml/XmlOutputGenerator.java index 5a765d4d6..49e578464 100644 --- a/japicmp/src/main/java/japicmp/output/xml/XmlOutputGenerator.java +++ b/japicmp/src/main/java/japicmp/output/xml/XmlOutputGenerator.java @@ -6,26 +6,20 @@ import japicmp.model.JApiClass; import japicmp.output.OutputFilter; import japicmp.output.OutputGenerator; -import japicmp.output.extapi.jpa.JpaAnalyzer; -import japicmp.output.extapi.jpa.model.JpaTable; import japicmp.output.xml.model.JApiCmpXmlRoot; import japicmp.util.Optional; -import japicmp.util.Streams; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.SchemaOutputResolver; -import javax.xml.transform.*; +import javax.xml.transform.Result; import javax.xml.transform.stream.StreamResult; -import javax.xml.transform.stream.StreamSource; -import java.io.*; -import java.nio.charset.Charset; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.nio.file.StandardOpenOption; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -54,7 +48,6 @@ public XmlOutputGenerator(List jApiClasses, Options options, XmlOutpu @Override public XmlOutput generate() { JApiCmpXmlRoot jApiCmpXmlRoot = createRootElement(jApiClasses, options); - //analyzeJpaAnnotations(jApiCmpXmlRoot, jApiClasses); filterClasses(jApiClasses, options); return createXmlDocumentAndSchema(options, jApiCmpXmlRoot); } @@ -72,16 +65,6 @@ public static List writeToFiles(Options options, XmlOutput xmlOutput) { throw new JApiCmpException(JApiCmpException.Reason.IoException, "Failed to write XML file '" + xmlFile.getAbsolutePath() + "': " + e.getMessage(), e); } } - if (xmlOutput.getHtmlOutputStream().isPresent() && options.getHtmlOutputFile().isPresent()) { - File htmlFile = new File(options.getHtmlOutputFile().get()); - try (FileOutputStream fos = new FileOutputStream(htmlFile)) { - ByteArrayOutputStream outputStream = xmlOutput.getHtmlOutputStream().get(); - outputStream.writeTo(fos); - filesWritten.add(htmlFile); - } catch (IOException e) { - throw new JApiCmpException(JApiCmpException.Reason.IoException, "Failed to write HTML file '" + htmlFile.getAbsolutePath() + "': " + e.getMessage(), e); - } - } } finally { try { xmlOutput.close(); @@ -92,18 +75,10 @@ public static List writeToFiles(Options options, XmlOutput xmlOutput) { return filesWritten; } - private void analyzeJpaAnnotations(JApiCmpXmlRoot jApiCmpXmlRoot, List jApiClasses) { - JpaAnalyzer jpaAnalyzer = new JpaAnalyzer(); - List jpaEntities = jpaAnalyzer.analyze(jApiClasses); - //jApiCmpXmlRoot.setJpaTables(jpaEntities); - } - private XmlOutput createXmlDocumentAndSchema(Options options, JApiCmpXmlRoot jApiCmpXmlRoot) { XmlOutput xmlOutput = new XmlOutput(); xmlOutput.setJApiCmpXmlRoot(jApiCmpXmlRoot); - ByteArrayOutputStream xmlBaos = null; - InputStream styleSheetAsInputStream = null; - InputStream xsltAsInputStream = null; + ByteArrayOutputStream xmlBaos; try { JAXBContext jaxbContext = JAXBContext.newInstance(JApiCmpXmlRoot.class); Marshaller marshaller = jaxbContext.createMarshaller(); @@ -118,7 +93,7 @@ private XmlOutput createXmlDocumentAndSchema(Options options, JApiCmpXmlRoot jAp final File xmlFile = new File(options.getXmlOutputFile().get()); SchemaOutputResolver outputResolver = new SchemaOutputResolver() { @Override - public Result createOutput(String namespaceUri, String suggestedFileName) throws IOException { + public Result createOutput(String namespaceUri, String suggestedFileName) { File schemaFile = xmlFile.getParentFile(); if (schemaFile == null) { LOGGER.warning(String.format("File '%s' has no parent file. Using instead: '%s'.", xmlFile.getAbsolutePath(), XSD_FILENAME)); @@ -134,64 +109,14 @@ public Result createOutput(String namespaceUri, String suggestedFileName) throws jaxbContext.generateSchema(outputResolver); } } - if (options.getHtmlOutputFile().isPresent()) { - TransformerFactory transformerFactory = TransformerFactory.newInstance(); - xsltAsInputStream = XmlOutputGenerator.class.getResourceAsStream("/html.xslt"); - if (xsltAsInputStream == null) { - throw new JApiCmpException(Reason.XsltError, "Failed to load XSLT."); - } - if (options.getHtmlStylesheet().isPresent()) { - styleSheetAsInputStream = new FileInputStream(options.getHtmlStylesheet().get()); - } else { - styleSheetAsInputStream = XmlOutputGenerator.class.getResourceAsStream("/style.css"); - if (styleSheetAsInputStream == null) { - throw new JApiCmpException(Reason.XsltError, "Failed to load stylesheet."); - } - } - String xsltAsString = integrateStylesheetIntoXslt(xsltAsInputStream, styleSheetAsInputStream); - Transformer transformer = transformerFactory.newTransformer(new StreamSource(new StringReader(xsltAsString))); - ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xmlBaos.toByteArray()); - ByteArrayOutputStream htmlOutputStream = new ByteArrayOutputStream(); - transformer.transform(new StreamSource(byteArrayInputStream), new StreamResult(htmlOutputStream)); - xmlOutput.setHtmlOutputStream(Optional.of(htmlOutputStream)); - } } catch (JAXBException e) { throw new JApiCmpException(Reason.JaxbException, String.format("Marshalling of XML document failed: %s", e.getMessage()), e); } catch (IOException e) { throw new JApiCmpException(Reason.IoException, String.format("Marshalling of XML document failed: %s", e.getMessage()), e); - } catch (TransformerConfigurationException e) { - throw new JApiCmpException(Reason.XsltError, String.format("Configuration of XSLT transformer failed: %s", e.getMessage()), e); - } catch (TransformerException e) { - throw new JApiCmpException(Reason.XsltError, String.format("XSLT transformation failed: %s", e.getMessage()), e); - } finally { - try { - if (styleSheetAsInputStream != null) { - styleSheetAsInputStream.close(); - } - if (xsltAsInputStream != null) { - xsltAsInputStream.close(); - } - } catch (IOException e) { - LOGGER.log(Level.FINE, "Failed to close CSS and/or XSLT file: " + e.getLocalizedMessage(), e); - } } return xmlOutput; } - private String integrateStylesheetIntoXslt(InputStream xsltAsInputStream, InputStream styleSheetAsInputStream) { - String xsltAsString = Streams.asString(xsltAsInputStream); - String styleSheetAsString = Streams.asString(styleSheetAsInputStream); - xsltAsString = xsltAsString.replace("", ""); - if (System.getProperty("japicmp.dump.xslt") != null) { - try { - Files.write(Paths.get(System.getProperty("japicmp.dump.xslt")), Collections.singletonList(xsltAsString), Charset.forName("UTF-8"), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); - } catch (IOException e) { - LOGGER.log(Level.WARNING, "Could not dump XSLT file: " + e.getMessage(), e); - } - } - return xsltAsString; - } - private void filterClasses(List jApiClasses, Options options) { OutputFilter outputFilter = new OutputFilter(options); outputFilter.filter(jApiClasses); @@ -228,6 +153,4 @@ private String regExAsString(List ignoreMissingClassRegularExpression) } return sb.toString(); } - - } diff --git a/japicmp/src/main/resources/html/class-entry.html b/japicmp/src/main/resources/html/class-entry.html index d32806fe6..b24be319e 100644 --- a/japicmp/src/main/resources/html/class-entry.html +++ b/japicmp/src/main/resources/html/class-entry.html @@ -1,7 +1,7 @@
- + ${outputChangeStatus} ${javaObjectSerializationCompatible} @@ -29,29 +29,8 @@ ${constructors}
- - Methods: - - - - - - - - - - - - - - - - - - -
StatusModifierGeneric TemplatesTypeMethodExceptionsCompatibility Changes:Line Number
-
+ ${methods}
- + ${annotations}
diff --git a/japicmp/src/main/resources/html/methods.html b/japicmp/src/main/resources/html/methods.html new file mode 100644 index 000000000..04ca943aa --- /dev/null +++ b/japicmp/src/main/resources/html/methods.html @@ -0,0 +1,18 @@ +Methods: + + + + + + + + + + + + + + + ${tbody} + +
StatusModifierGeneric TemplatesTypeMethodExceptionsCompatibility Changes:Line Number
diff --git a/japicmp/src/test/java/japicmp/output/html/HtmlOutputGeneratorTest.java b/japicmp/src/test/java/japicmp/output/html/HtmlOutputGeneratorTest.java index 3267313e7..87509501d 100644 --- a/japicmp/src/test/java/japicmp/output/html/HtmlOutputGeneratorTest.java +++ b/japicmp/src/test/java/japicmp/output/html/HtmlOutputGeneratorTest.java @@ -4,6 +4,9 @@ import japicmp.cmp.JarArchiveComparatorOptions; import japicmp.config.Options; import japicmp.model.JApiClass; +import japicmp.output.xml.XmlOutput; +import japicmp.output.xml.XmlOutputGenerator; +import japicmp.output.xml.XmlOutputGeneratorOptions; import japicmp.util.*; import javassist.CannotCompileException; import javassist.ClassPool; @@ -34,7 +37,7 @@ public void testHtmlReport() throws Exception { @Override public List createOldClasses(ClassPool classPool) throws CannotCompileException { CtClass ctClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool); - CtMethodBuilder.create().name("toBeRemoved").returnType(CtClass.booleanType).addToClass(ctClass); + CtMethodBuilder.create().publicAccess().name("toBeRemoved").returnType(CtClass.booleanType).addToClass(ctClass); return Collections.singletonList(ctClass); } @@ -43,7 +46,7 @@ public List createNewClasses(ClassPool classPool) throws CannotCompileE CtClass newInterface = CtInterfaceBuilder.create().name("NewInterface").addToClassPool(classPool); CtClass superclass = CtClassBuilder.create().name("japicmp.Superclass").addToClassPool(classPool); CtClass ctClass = CtClassBuilder.create().name("japicmp.Test").withSuperclass(superclass).implementsInterface(newInterface).addToClassPool(classPool); - CtMethodBuilder.create().name("newMethod").returnType(CtClass.booleanType).addToClass(ctClass); + CtMethodBuilder.create().publicAccess().name("newMethod").returnType(CtClass.booleanType).addToClass(ctClass); CtFieldBuilder.create().type(CtClass.booleanType).name("bField").addToClass(ctClass); CtConstructorBuilder.create().publicAccess().parameters(new CtClass[] {CtClass.intType, CtClass.booleanType}).exceptions(new CtClass[] {classPool.get("java.lang.Exception")}).addToClass(ctClass); return Arrays.asList(ctClass, superclass); @@ -60,4 +63,37 @@ public List createNewClasses(ClassPool classPool) throws CannotCompileE assertThat(document.select("#meta-accessmodifier-value").text(), is("PROTECTED")); assertThat(document.select("#warning-missingclasses").text(), containsString("WARNING")); } + + @Test + public void testXmlReport() throws Exception { + JarArchiveComparatorOptions options = new JarArchiveComparatorOptions(); + options.setIncludeSynthetic(true); + List jApiClasses = ClassesHelper.compareClasses(options, new ClassesHelper.ClassesGenerator() { + @Override + public List createOldClasses(ClassPool classPool) throws CannotCompileException { + CtClass ctClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool); + CtMethodBuilder.create().publicAccess().name("toBeRemoved").returnType(CtClass.booleanType).addToClass(ctClass); + return Collections.singletonList(ctClass); + } + + @Override + public List createNewClasses(ClassPool classPool) throws CannotCompileException, NotFoundException { + CtClass newInterface = CtInterfaceBuilder.create().name("NewInterface").addToClassPool(classPool); + CtClass superclass = CtClassBuilder.create().name("japicmp.Superclass").addToClassPool(classPool); + CtClass ctClass = CtClassBuilder.create().name("japicmp.Test").withSuperclass(superclass).implementsInterface(newInterface).addToClassPool(classPool); + CtMethodBuilder.create().publicAccess().name("newMethod").returnType(CtClass.booleanType).addToClass(ctClass); + CtFieldBuilder.create().type(CtClass.booleanType).name("bField").addToClass(ctClass); + CtConstructorBuilder.create().publicAccess().parameters(new CtClass[] {CtClass.intType, CtClass.booleanType}).exceptions(new CtClass[] {classPool.get("java.lang.Exception")}).addToClass(ctClass); + return Arrays.asList(ctClass, superclass); + } + }); + Options reportOptions = Options.newDefault(); + reportOptions.setIgnoreMissingClasses(true); + reportOptions.setXmlOutputFile(Optional.of(Paths.get(System.getProperty("user.dir"), "target", "report.xml").toString())); + XmlOutputGenerator generator = new XmlOutputGenerator(jApiClasses, reportOptions, new XmlOutputGeneratorOptions()); + + XmlOutput xmlOutput = generator.generate(); + + Files.write(Paths.get(System.getProperty("user.dir"), "target", "report.xml"), xmlOutput.getXmlOutputStream().get().toString().getBytes(StandardCharsets.UTF_8)); + } } From 37e7bed03da58c7b1ba119e83996a2f28ffe2b50 Mon Sep 17 00:00:00 2001 From: Martin Mois Date: Mon, 4 Mar 2024 22:54:11 +0100 Subject: [PATCH 3/7] fixed XmlOutputGeneratorTest --- .../output/html/HtmlOutputGeneratorTest.java | 36 ------- .../output/xml/XmlOutputGeneratorTest.java | 94 +++++++------------ 2 files changed, 32 insertions(+), 98 deletions(-) diff --git a/japicmp/src/test/java/japicmp/output/html/HtmlOutputGeneratorTest.java b/japicmp/src/test/java/japicmp/output/html/HtmlOutputGeneratorTest.java index 87509501d..9a27159e1 100644 --- a/japicmp/src/test/java/japicmp/output/html/HtmlOutputGeneratorTest.java +++ b/japicmp/src/test/java/japicmp/output/html/HtmlOutputGeneratorTest.java @@ -4,9 +4,6 @@ import japicmp.cmp.JarArchiveComparatorOptions; import japicmp.config.Options; import japicmp.model.JApiClass; -import japicmp.output.xml.XmlOutput; -import japicmp.output.xml.XmlOutputGenerator; -import japicmp.output.xml.XmlOutputGeneratorOptions; import japicmp.util.*; import javassist.CannotCompileException; import javassist.ClassPool; @@ -63,37 +60,4 @@ public List createNewClasses(ClassPool classPool) throws CannotCompileE assertThat(document.select("#meta-accessmodifier-value").text(), is("PROTECTED")); assertThat(document.select("#warning-missingclasses").text(), containsString("WARNING")); } - - @Test - public void testXmlReport() throws Exception { - JarArchiveComparatorOptions options = new JarArchiveComparatorOptions(); - options.setIncludeSynthetic(true); - List jApiClasses = ClassesHelper.compareClasses(options, new ClassesHelper.ClassesGenerator() { - @Override - public List createOldClasses(ClassPool classPool) throws CannotCompileException { - CtClass ctClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool); - CtMethodBuilder.create().publicAccess().name("toBeRemoved").returnType(CtClass.booleanType).addToClass(ctClass); - return Collections.singletonList(ctClass); - } - - @Override - public List createNewClasses(ClassPool classPool) throws CannotCompileException, NotFoundException { - CtClass newInterface = CtInterfaceBuilder.create().name("NewInterface").addToClassPool(classPool); - CtClass superclass = CtClassBuilder.create().name("japicmp.Superclass").addToClassPool(classPool); - CtClass ctClass = CtClassBuilder.create().name("japicmp.Test").withSuperclass(superclass).implementsInterface(newInterface).addToClassPool(classPool); - CtMethodBuilder.create().publicAccess().name("newMethod").returnType(CtClass.booleanType).addToClass(ctClass); - CtFieldBuilder.create().type(CtClass.booleanType).name("bField").addToClass(ctClass); - CtConstructorBuilder.create().publicAccess().parameters(new CtClass[] {CtClass.intType, CtClass.booleanType}).exceptions(new CtClass[] {classPool.get("java.lang.Exception")}).addToClass(ctClass); - return Arrays.asList(ctClass, superclass); - } - }); - Options reportOptions = Options.newDefault(); - reportOptions.setIgnoreMissingClasses(true); - reportOptions.setXmlOutputFile(Optional.of(Paths.get(System.getProperty("user.dir"), "target", "report.xml").toString())); - XmlOutputGenerator generator = new XmlOutputGenerator(jApiClasses, reportOptions, new XmlOutputGeneratorOptions()); - - XmlOutput xmlOutput = generator.generate(); - - Files.write(Paths.get(System.getProperty("user.dir"), "target", "report.xml"), xmlOutput.getXmlOutputStream().get().toString().getBytes(StandardCharsets.UTF_8)); - } } diff --git a/japicmp/src/test/java/japicmp/output/xml/XmlOutputGeneratorTest.java b/japicmp/src/test/java/japicmp/output/xml/XmlOutputGeneratorTest.java index 21568175b..4c58882b1 100644 --- a/japicmp/src/test/java/japicmp/output/xml/XmlOutputGeneratorTest.java +++ b/japicmp/src/test/java/japicmp/output/xml/XmlOutputGeneratorTest.java @@ -1,85 +1,55 @@ package japicmp.output.xml; -import japicmp.cmp.JarArchiveComparator; +import japicmp.cmp.ClassesHelper; import japicmp.cmp.JarArchiveComparatorOptions; import japicmp.config.Options; -import japicmp.exception.JApiCmpException; -import japicmp.model.JApiChangeStatus; import japicmp.model.JApiClass; -import japicmp.model.JApiClassType; -import japicmp.util.Optional; +import japicmp.util.*; +import javassist.CannotCompileException; +import javassist.ClassPool; import javassist.CtClass; +import javassist.NotFoundException; import org.junit.Test; -import java.io.IOException; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.Paths; -import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; - public class XmlOutputGeneratorTest { @Test - public void testWithHtmlStylesheetOption() throws IOException { - Path stylesheetPath = Paths.get(System.getProperty("user.dir"), "target", XmlOutputGeneratorTest.class.getSimpleName() + "_with.css"); - Path htmlReportPath = Paths.get(System.getProperty("user.dir"), "target", XmlOutputGeneratorTest.class.getSimpleName() + "_with.html"); - String stylesheetContent = "body {font-family: Monospace;}"; - Options options = Options.newDefault(); - options.setHtmlStylesheet(Optional.of(stylesheetPath.toString())); - options.setHtmlOutputFile(Optional.of(htmlReportPath.toString())); - Files.write(stylesheetPath, Collections.singletonList(stylesheetContent), Charset.forName("UTF-8")); - generateHtmlReport(options); - boolean foundStyleSheet = false; - List lines = Files.readAllLines(htmlReportPath, Charset.forName("UTF-8")); - for (String line : lines) { - if (line.contains(stylesheetContent)) { - foundStyleSheet = true; + public void testXmlReport() throws Exception { + JarArchiveComparatorOptions options = new JarArchiveComparatorOptions(); + options.setIncludeSynthetic(true); + List jApiClasses = ClassesHelper.compareClasses(options, new ClassesHelper.ClassesGenerator() { + @Override + public List createOldClasses(ClassPool classPool) throws CannotCompileException { + CtClass ctClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool); + CtMethodBuilder.create().publicAccess().name("toBeRemoved").returnType(CtClass.booleanType).addToClass(ctClass); + return Collections.singletonList(ctClass); } - } - assertThat(foundStyleSheet, is(true)); - } - @Test - public void testWithoutHtmlStylesheetOption() throws IOException { - Path htmlReportPath = Paths.get(System.getProperty("user.dir"), "target", XmlOutputGeneratorTest.class.getSimpleName() + "_without.html"); - Options options = Options.newDefault(); - options.setHtmlOutputFile(Optional.of(htmlReportPath.toString())); - generateHtmlReport(options); - boolean foundStyleSheet = false; - List lines = Files.readAllLines(htmlReportPath, Charset.forName("UTF-8")); - for (String line : lines) { - if (line.contains("font-family: Verdana;")) { - foundStyleSheet = true; + @Override + public List createNewClasses(ClassPool classPool) throws CannotCompileException, NotFoundException { + CtClass newInterface = CtInterfaceBuilder.create().name("NewInterface").addToClassPool(classPool); + CtClass superclass = CtClassBuilder.create().name("japicmp.Superclass").addToClassPool(classPool); + CtClass ctClass = CtClassBuilder.create().name("japicmp.Test").withSuperclass(superclass).implementsInterface(newInterface).addToClassPool(classPool); + CtMethodBuilder.create().publicAccess().name("newMethod").returnType(CtClass.booleanType).addToClass(ctClass); + CtFieldBuilder.create().type(CtClass.booleanType).name("bField").addToClass(ctClass); + CtConstructorBuilder.create().publicAccess().parameters(new CtClass[] {CtClass.intType, CtClass.booleanType}).exceptions(new CtClass[] {classPool.get("java.lang.Exception")}).addToClass(ctClass); + return Arrays.asList(ctClass, superclass); } - } - assertThat(foundStyleSheet, is(true)); - } + }); + Options reportOptions = Options.newDefault(); + reportOptions.setIgnoreMissingClasses(true); + reportOptions.setXmlOutputFile(Optional.of(Paths.get(System.getProperty("user.dir"), "target", "report.xml").toString())); + XmlOutputGenerator generator = new XmlOutputGenerator(jApiClasses, reportOptions, new XmlOutputGeneratorOptions()); - @Test(expected = JApiCmpException.class) - public void testWithNotExistingHtmlStylesheetOption() throws IOException { - Path stylesheetPath = Paths.get(System.getProperty("user.dir"), "target", XmlOutputGeneratorTest.class.getSimpleName() + "_not_existing.css"); - Path htmlReportPath = Paths.get(System.getProperty("user.dir"), "target", XmlOutputGeneratorTest.class.getSimpleName() + "_with.html"); - Options options = Options.newDefault(); - options.setHtmlStylesheet(Optional.of(stylesheetPath.toString())); - options.setHtmlOutputFile(Optional.of(htmlReportPath.toString())); - generateHtmlReport(options); - } - - private void generateHtmlReport(Options options) { - List jApiClasses = new ArrayList<>(); - JarArchiveComparatorOptions jarArchiveComparatorOptions = JarArchiveComparatorOptions.of(options); - JApiClassType classType = new JApiClassType(Optional.absent(), Optional.absent(), JApiChangeStatus.REMOVED); - jApiClasses.add(new JApiClass(new JarArchiveComparator(jarArchiveComparatorOptions), "japicmp.Test", Optional.absent(), Optional.absent(), JApiChangeStatus.NEW, classType)); - XmlOutputGeneratorOptions xmlOutputGeneratorOptions = new XmlOutputGeneratorOptions(); - xmlOutputGeneratorOptions.setCreateSchemaFile(true); - XmlOutputGenerator generator = new XmlOutputGenerator(jApiClasses, options, xmlOutputGeneratorOptions); XmlOutput xmlOutput = generator.generate(); - XmlOutputGenerator.writeToFiles(options, xmlOutput); + + Files.write(Paths.get(System.getProperty("user.dir"), "target", "report.xml"), xmlOutput.getXmlOutputStream().get().toString().getBytes(StandardCharsets.UTF_8)); } } From 975fd4c3e3f1ef4e528582444574d81cabb0ae0d Mon Sep 17 00:00:00 2001 From: Martin Mois Date: Mon, 4 Mar 2024 23:11:27 +0100 Subject: [PATCH 4/7] fixed further tests --- .../japicmp-test-maven-plugin/pom.xml | 6 +- .../XmlOutputGeneratorAccessModifierTest.java | 14 ++--- .../xml/XmlOutputGeneratorClassTypeTest.java | 6 +- .../output/xml/XmlOutputGeneratorTest.java | 61 +++++++++---------- .../test/java/japicmp/test/util/Helper.java | 32 +++++----- 5 files changed, 54 insertions(+), 65 deletions(-) diff --git a/japicmp-testbase/japicmp-test-maven-plugin/pom.xml b/japicmp-testbase/japicmp-test-maven-plugin/pom.xml index 1719fd0b9..04b0c2e9b 100644 --- a/japicmp-testbase/japicmp-test-maven-plugin/pom.xml +++ b/japicmp-testbase/japicmp-test-maven-plugin/pom.xml @@ -104,11 +104,9 @@ true public false - false - + false true - ${project.basedir}/src/main/resources/css/stylesheet.css - + ${project.basedir}/src/main/resources/css/stylesheet.css Test-Title diff --git a/japicmp-testbase/japicmp-test/src/test/java/japicmp/test/output/xml/XmlOutputGeneratorAccessModifierTest.java b/japicmp-testbase/japicmp-test/src/test/java/japicmp/test/output/xml/XmlOutputGeneratorAccessModifierTest.java index 99f2d6198..a233ed460 100644 --- a/japicmp-testbase/japicmp-test/src/test/java/japicmp/test/output/xml/XmlOutputGeneratorAccessModifierTest.java +++ b/japicmp-testbase/japicmp-test/src/test/java/japicmp/test/output/xml/XmlOutputGeneratorAccessModifierTest.java @@ -12,14 +12,14 @@ import java.io.File; import java.io.IOException; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; import static japicmp.test.util.Helper.replaceLastDotWith$; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; public class XmlOutputGeneratorAccessModifierTest { private static Document documentPublic; @@ -27,18 +27,16 @@ public class XmlOutputGeneratorAccessModifierTest { @BeforeClass public static void beforeClass() throws IOException { - Path diffPublicXmlFilePath = Paths.get(System.getProperty("user.dir"), "target", "diff_public.xml"); Path diffPublicHtmlFilePath = Paths.get(System.getProperty("user.dir"), "target", "diff_public.html"); - Path diffPrivateXmlFilePath = Paths.get(System.getProperty("user.dir"), "target", "diff_private.xml"); Path diffPrivateHtmlFilePath = Paths.get(System.getProperty("user.dir"), "target", "diff_private.html"); List jApiClasses = Helper.compareTestV1WithTestV2(AccessModifier.PUBLIC); - Helper.generateHtmlOutput(jApiClasses, diffPublicXmlFilePath.toString(), diffPublicHtmlFilePath.toString(), true, AccessModifier.PUBLIC); + Helper.generateHtmlOutput(jApiClasses, diffPublicHtmlFilePath.toString(), true, AccessModifier.PUBLIC); jApiClasses = Helper.compareTestV1WithTestV2(AccessModifier.PRIVATE); - Helper.generateHtmlOutput(jApiClasses, diffPrivateXmlFilePath.toString(), diffPrivateHtmlFilePath.toString(), true, AccessModifier.PRIVATE); + Helper.generateHtmlOutput(jApiClasses, diffPrivateHtmlFilePath.toString(), true, AccessModifier.PRIVATE); File htmlFilePublic = diffPublicHtmlFilePath.toFile(); File htmlFilePrivate = diffPrivateHtmlFilePath.toFile(); - documentPublic = Jsoup.parse(htmlFilePublic, Charset.forName("UTF-8").toString()); - documentPrivate = Jsoup.parse(htmlFilePrivate, Charset.forName("UTF-8").toString()); + documentPublic = Jsoup.parse(htmlFilePublic, StandardCharsets.UTF_8.toString()); + documentPrivate = Jsoup.parse(htmlFilePrivate, StandardCharsets.UTF_8.toString()); } @Test diff --git a/japicmp-testbase/japicmp-test/src/test/java/japicmp/test/output/xml/XmlOutputGeneratorClassTypeTest.java b/japicmp-testbase/japicmp-test/src/test/java/japicmp/test/output/xml/XmlOutputGeneratorClassTypeTest.java index 9fdfb239f..066327743 100644 --- a/japicmp-testbase/japicmp-test/src/test/java/japicmp/test/output/xml/XmlOutputGeneratorClassTypeTest.java +++ b/japicmp-testbase/japicmp-test/src/test/java/japicmp/test/output/xml/XmlOutputGeneratorClassTypeTest.java @@ -13,7 +13,7 @@ import java.io.File; import java.io.IOException; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; @@ -31,9 +31,9 @@ public static void beforeClass() throws IOException { Path diffPublicXmlPath = Paths.get(System.getProperty("user.dir"), "target", "diff_public.xml"); Path diffPublicHtmlPath = Paths.get(System.getProperty("user.dir"), "target", "diff_public.html"); List jApiClasses = Helper.compareTestV1WithTestV2(AccessModifier.PUBLIC); - Helper.generateHtmlOutput(jApiClasses, diffPublicXmlPath.toString(), diffPublicHtmlPath.toString(), false, AccessModifier.PUBLIC); + Helper.generateHtmlOutput(jApiClasses, diffPublicHtmlPath.toString(), false, AccessModifier.PUBLIC); File htmlFilePublic = Paths.get(System.getProperty("user.dir"), "target", "diff_public.html").toFile(); - documentPublic = Jsoup.parse(htmlFilePublic, Charset.forName("UTF-8").toString()); + documentPublic = Jsoup.parse(htmlFilePublic, StandardCharsets.UTF_8.toString()); } @Test diff --git a/japicmp-testbase/japicmp-test/src/test/java/japicmp/test/output/xml/XmlOutputGeneratorTest.java b/japicmp-testbase/japicmp-test/src/test/java/japicmp/test/output/xml/XmlOutputGeneratorTest.java index 391fea580..3aa424271 100644 --- a/japicmp-testbase/japicmp-test/src/test/java/japicmp/test/output/xml/XmlOutputGeneratorTest.java +++ b/japicmp-testbase/japicmp-test/src/test/java/japicmp/test/output/xml/XmlOutputGeneratorTest.java @@ -1,14 +1,13 @@ package japicmp.test.output.xml; -import com.google.common.io.Files; import japicmp.cmp.JarArchiveComparator; import japicmp.cmp.JarArchiveComparatorOptions; import japicmp.config.Options; import japicmp.filter.JavadocLikePackageFilter; import japicmp.model.JApiClass; -import japicmp.output.xml.XmlOutput; -import japicmp.output.xml.XmlOutputGenerator; -import japicmp.output.xml.XmlOutputGeneratorOptions; +import japicmp.output.html.HtmlOutput; +import japicmp.output.html.HtmlOutputGenerator; +import japicmp.output.html.HtmlOutputGeneratorOptions; import japicmp.util.Optional; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; @@ -18,15 +17,16 @@ import java.io.File; import java.io.IOException; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; import static japicmp.test.output.xml.XmlHelper.getDivForClass; import static japicmp.test.util.Helper.getArchive; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; public class XmlOutputGeneratorTest { private static final String JAPICMP_TEST_SEMVER001 = "japicmp.test.semver001"; @@ -38,42 +38,39 @@ public class XmlOutputGeneratorTest { @BeforeClass public static void beforeClass() throws IOException { - Path diffXmlFilePath = Paths.get(System.getProperty("user.dir"), "target", "diff.xml"); Path diffHtmlFilePath = Paths.get(System.getProperty("user.dir"), "target", "diff.html"); - Path diffOnlyModificationsXmlFilePath = Paths.get(System.getProperty("user.dir"), "target", "diff_onlyModifications.xml"); Path diffOnlyModificationsHtmlFilePath = Paths.get(System.getProperty("user.dir"), "target", "diff_onlyModifications.html"); JarArchiveComparatorOptions options = new JarArchiveComparatorOptions(); options.getFilters().getExcludes().add(new JavadocLikePackageFilter(JAPICMP_TEST_SEMVER001, false)); JarArchiveComparator jarArchiveComparator = new JarArchiveComparator(options); jApiClasses = jarArchiveComparator.compare(getArchive("japicmp-test-v1.jar"), getArchive("japicmp-test-v2.jar")); - generateHtmlOutput(diffXmlFilePath.toString(), diffHtmlFilePath.toString(), false); - generateHtmlOutput(diffOnlyModificationsXmlFilePath.toString(), diffOnlyModificationsHtmlFilePath.toString(), true); + generateHtmlOutput(diffHtmlFilePath.toString(), false); + generateHtmlOutput(diffOnlyModificationsHtmlFilePath.toString(), true); htmlFile = Paths.get(System.getProperty("user.dir"), "target", "diff.html").toFile(); - document = Jsoup.parse(htmlFile, Charset.forName("UTF-8").toString()); - documentOnlyModifications = Jsoup.parse(diffOnlyModificationsHtmlFilePath.toFile(), Charset.forName("UTF-8").toString()); + document = Jsoup.parse(htmlFile, StandardCharsets.UTF_8.toString()); + documentOnlyModifications = Jsoup.parse(diffOnlyModificationsHtmlFilePath.toFile(), StandardCharsets.UTF_8.toString()); } - private static void generateHtmlOutput(String xmlOutpuFile, String htmlOutputFile, boolean outputOnlyModifications) { + private static void generateHtmlOutput( String htmlOutputFile, boolean outputOnlyModifications) throws IOException { Options options = Options.newDefault(); - options.setXmlOutputFile(Optional.of(xmlOutpuFile)); options.setHtmlOutputFile(Optional.of(htmlOutputFile)); options.setOutputOnlyModifications(outputOnlyModifications); - XmlOutputGeneratorOptions xmlOutputGeneratorOptions = new XmlOutputGeneratorOptions(); - xmlOutputGeneratorOptions.setTitle(TITLE); - XmlOutputGenerator generator = new XmlOutputGenerator(jApiClasses, options, xmlOutputGeneratorOptions); - XmlOutput xmlOutput = generator.generate(); - XmlOutputGenerator.writeToFiles(options, xmlOutput); + HtmlOutputGeneratorOptions htmlOutputGeneratorOptions = new HtmlOutputGeneratorOptions(); + htmlOutputGeneratorOptions.setTitle(TITLE); + HtmlOutputGenerator generator = new HtmlOutputGenerator(jApiClasses, options, htmlOutputGeneratorOptions); + HtmlOutput htmlOutput = generator.generate(); + Files.write(Paths.get(htmlOutputFile), htmlOutput.getHtml().getBytes(StandardCharsets.UTF_8)); } @Test - public void testMetaInformationTable() throws IOException { + public void testMetaInformationTable() { assertThat(document.select("div.meta-information > table").isEmpty(), is(false)); assertThat(document.select("div.meta-information > table > tbody > tr").size(), is(10)); } @Test public void htmlFileNotContainsPackageSemver001() throws IOException { - List lines = Files.readLines(htmlFile, Charset.forName("UTF-8")); + List lines = Files.readAllLines(htmlFile.toPath(), StandardCharsets.UTF_8); boolean containsPackageName = false; for (String line : lines) { if (line.contains(JAPICMP_TEST_SEMVER001)) { @@ -85,13 +82,13 @@ public void htmlFileNotContainsPackageSemver001() throws IOException { } @Test - public void superclassAllChangesAddedWithSuperclass() throws IOException { + public void superclassAllChangesAddedWithSuperclass() { Elements divSuperClass = getSuperClassDiv(document, "japicmp.test.Superclasses$AddedWithSuperclass"); assertThat(divSuperClass.select("table").isEmpty(), is(false)); } @Test - public void superclassOnlyModificationsAddedWithSuperclass() throws IOException { + public void superclassOnlyModificationsAddedWithSuperclass() { Elements divSuperClass = getSuperClassDiv(documentOnlyModifications, "japicmp.test.Superclasses$AddedWithSuperclass"); assertThat(divSuperClass.select("table").isEmpty(), is(false)); } @@ -104,49 +101,49 @@ private Elements getSuperClassDiv(Document document, String className) { } @Test - public void superclassAllChangesAdded() throws IOException { - Elements divSuperClass = getSuperClassDiv(document, "japicmp.test.Added"); + public void superclassAllChangesAdded() { + Elements divSuperClass = getSuperClassDiv(document, "japicmp.test.Superclasses$AddedWithSuperclass"); assertThat(divSuperClass.select("table").isEmpty(), is(false)); } @Test - public void superclassOnlyModificationsAdded() throws IOException { + public void superclassOnlyModificationsAdded() { Elements divSuperClass = getSuperClassDiv(documentOnlyModifications, "japicmp.test.Added"); assertThat(divSuperClass.select("table").isEmpty(), is(true)); } @Test - public void superclassAddedWithSuperclass() throws IOException { + public void superclassAddedWithSuperclass() { Elements divSuperClass = getSuperClassDiv(document, "japicmp.test.Superclasses$AddedWithSuperclass"); assertThat(divSuperClass.select("table").isEmpty(), is(false)); } @Test - public void superclassAddedWithSuperclassOnlyModifications() throws IOException { + public void superclassAddedWithSuperclassOnlyModifications() { Elements divSuperClass = getSuperClassDiv(documentOnlyModifications, "japicmp.test.Superclasses$AddedWithSuperclass"); assertThat(divSuperClass.select("table").isEmpty(), is(false)); } @Test - public void superclassRemovedWithSuperclass() throws IOException { + public void superclassRemovedWithSuperclass() { Elements divSuperClass = getSuperClassDiv(document, "japicmp.test.Superclasses$RemovedWithSuperclass"); assertThat(divSuperClass.select("table").isEmpty(), is(false)); } @Test - public void superclassRemovedWithSuperclassOnlyModifications() throws IOException { + public void superclassRemovedWithSuperclassOnlyModifications() { Elements divSuperClass = getSuperClassDiv(documentOnlyModifications, "japicmp.test.Superclasses$RemovedWithSuperclass"); assertThat(divSuperClass.select("table").isEmpty(), is(false)); } @Test - public void superclassNoSuperclassToSuperclass() throws IOException { + public void superclassNoSuperclassToSuperclass() { Elements divSuperClass = getSuperClassDiv(document, "japicmp.test.Superclasses$NoSuperclassToSuperclass"); assertThat(divSuperClass.select("table").isEmpty(), is(false)); } @Test - public void superclassNoSuperclassToSuperclassOnlyModifications() throws IOException { + public void superclassNoSuperclassToSuperclassOnlyModifications() { Elements divSuperClass = getSuperClassDiv(documentOnlyModifications, "japicmp.test.Superclasses$NoSuperclassToSuperclass"); assertThat(divSuperClass.select("table").isEmpty(), is(false)); } diff --git a/japicmp-testbase/japicmp-test/src/test/java/japicmp/test/util/Helper.java b/japicmp-testbase/japicmp-test/src/test/java/japicmp/test/util/Helper.java index c191f99fa..382ebe91e 100644 --- a/japicmp-testbase/japicmp-test/src/test/java/japicmp/test/util/Helper.java +++ b/japicmp-testbase/japicmp-test/src/test/java/japicmp/test/util/Helper.java @@ -1,20 +1,14 @@ package japicmp.test.util; -import japicmp.util.Optional; import japicmp.cmp.JApiCmpArchive; import japicmp.cmp.JarArchiveComparator; import japicmp.cmp.JarArchiveComparatorOptions; import japicmp.config.Options; -import japicmp.model.AccessModifier; -import japicmp.model.JApiAnnotation; -import japicmp.model.JApiAnnotationElement; -import japicmp.model.JApiClass; -import japicmp.model.JApiField; -import japicmp.model.JApiImplementedInterface; -import japicmp.model.JApiMethod; -import japicmp.output.xml.XmlOutput; -import japicmp.output.xml.XmlOutputGenerator; -import japicmp.output.xml.XmlOutputGeneratorOptions; +import japicmp.model.*; +import japicmp.output.html.HtmlOutput; +import japicmp.output.html.HtmlOutputGenerator; +import japicmp.output.html.HtmlOutputGeneratorOptions; +import japicmp.util.Optional; import org.hamcrest.CoreMatchers; import org.hamcrest.Description; import org.hamcrest.Matcher; @@ -22,6 +16,10 @@ import org.junit.Assert; import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.List; public class Helper { @@ -164,17 +162,15 @@ public static List compareTestV1WithTestV2(AccessModifier accessModif return jarArchiveComparator.compare(getArchive("japicmp-test-v1.jar"), getArchive("japicmp-test-v2.jar")); } - public static void generateHtmlOutput(List jApiClasses, String xmlOutputFile, String htmlOutputFile, boolean outputOnlyModifications, AccessModifier accessModifier) { + public static void generateHtmlOutput(List jApiClasses, String htmlOutputFile, boolean outputOnlyModifications, AccessModifier accessModifier) throws IOException { Options options = Options.newDefault(); - options.setXmlOutputFile(Optional.of(xmlOutputFile)); options.setHtmlOutputFile(Optional.of(htmlOutputFile)); options.setOutputOnlyModifications(outputOnlyModifications); options.setAccessModifier(Optional.of(accessModifier)); - XmlOutputGeneratorOptions xmlOutputGeneratorOptions = new XmlOutputGeneratorOptions(); - xmlOutputGeneratorOptions.setCreateSchemaFile(true); - XmlOutputGenerator generator = new XmlOutputGenerator(jApiClasses, options, xmlOutputGeneratorOptions); - XmlOutput xmlOutput = generator.generate(); - XmlOutputGenerator.writeToFiles(options, xmlOutput); + HtmlOutputGeneratorOptions htmlOutputGeneratorOptions = new HtmlOutputGeneratorOptions(); + HtmlOutputGenerator generator = new HtmlOutputGenerator(jApiClasses, options, htmlOutputGeneratorOptions); + HtmlOutput htmlOutput = generator.generate(); + Files.write(Paths.get(htmlOutputFile), htmlOutput.getHtml().getBytes(StandardCharsets.UTF_8)); } public interface SimpleExceptionVerifier { From 230e5723b00cda2f8c0236f76ec7ce972189e4af Mon Sep 17 00:00:00 2001 From: Martin Mois Date: Tue, 5 Mar 2024 17:33:51 +0100 Subject: [PATCH 5/7] JApiCmpMojo uses HtmlOutputGenerator --- .../main/java/japicmp/ant/JApiCmpTask.java | 37 +++++-- .../java/japicmp/ant/JApiCmpTaskTest.java | 2 +- .../main/java/japicmp/maven/JApiCmpMojo.java | 104 ++++++++++++------ .../java/japicmp/maven/JApiCmpReport.java | 66 ++++++----- .../test/ITClassFileFormatVersion.java | 10 +- .../test/java/japicmp/test/ITStylesheet.java | 8 +- .../main/java/japicmp/cmp/JApiCmpArchive.java | 13 ++- .../src/main/java/japicmp/config/Options.java | 43 ++++---- .../output/html/HtmlOutputGenerator.java | 82 ++++++-------- .../japicmp/output/html/TemplateEngine.java | 33 ++++++ .../java/japicmp/output/xml/XmlOutput.java | 14 +-- 11 files changed, 242 insertions(+), 170 deletions(-) create mode 100644 japicmp/src/main/java/japicmp/output/html/TemplateEngine.java diff --git a/japicmp-ant-task/src/main/java/japicmp/ant/JApiCmpTask.java b/japicmp-ant-task/src/main/java/japicmp/ant/JApiCmpTask.java index 8268bc9ba..51fa4e57f 100644 --- a/japicmp-ant-task/src/main/java/japicmp/ant/JApiCmpTask.java +++ b/japicmp-ant-task/src/main/java/japicmp/ant/JApiCmpTask.java @@ -5,6 +5,9 @@ import japicmp.config.Options; import japicmp.exception.JApiCmpException; import japicmp.model.JApiClass; +import japicmp.output.html.HtmlOutput; +import japicmp.output.html.HtmlOutputGenerator; +import japicmp.output.html.HtmlOutputGeneratorOptions; import japicmp.output.incompatible.IncompatibleErrorOutput; import japicmp.output.semver.SemverOut; import japicmp.output.stdout.StdoutOutputGenerator; @@ -18,6 +21,10 @@ import org.apache.tools.ant.types.Path; import org.apache.tools.ant.types.Reference; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -280,14 +287,28 @@ private void generateOutput(Options options, List jApiClasses, JarArc } 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 BuildException("Could not close output streams: " + e.getMessage(), e); + String semanticVersioningInformation = semverOut.generate(); + if (options.getXmlOutputFile().isPresent()) { + XmlOutputGeneratorOptions xmlOutputGeneratorOptions = new XmlOutputGeneratorOptions(); + xmlOutputGeneratorOptions.setCreateSchemaFile(true); + xmlOutputGeneratorOptions.setSemanticVersioningInformation(semanticVersioningInformation); + XmlOutputGenerator xmlGenerator = new XmlOutputGenerator(jApiClasses, options, xmlOutputGeneratorOptions); + try (XmlOutput xmlOutput = xmlGenerator.generate()) { + XmlOutputGenerator.writeToFiles(options, xmlOutput); + } catch (Exception e) { + throw new BuildException("Writing XML report failed: " + e.getMessage(), e); + } + } + if (options.getHtmlOutputFile().isPresent()) { + HtmlOutputGeneratorOptions htmlOutputGeneratorOptions = new HtmlOutputGeneratorOptions(); + htmlOutputGeneratorOptions.setSemanticVersioningInformation(semanticVersioningInformation); + HtmlOutputGenerator htmlOutputGenerator = new HtmlOutputGenerator(jApiClasses, options, htmlOutputGeneratorOptions); + HtmlOutput htmlOutput = htmlOutputGenerator.generate(); + try { + Files.write(Paths.get(options.getHtmlOutputFile().get()), htmlOutput.getHtml().getBytes(StandardCharsets.UTF_8)); + } catch (IOException e) { + throw new BuildException("Writing HTML report failed: " + e.getMessage(), e); + } } } } diff --git a/japicmp-ant-task/src/test/java/japicmp/ant/JApiCmpTaskTest.java b/japicmp-ant-task/src/test/java/japicmp/ant/JApiCmpTaskTest.java index 507cf9e96..9c662f81f 100644 --- a/japicmp-ant-task/src/test/java/japicmp/ant/JApiCmpTaskTest.java +++ b/japicmp-ant-task/src/test/java/japicmp/ant/JApiCmpTaskTest.java @@ -7,7 +7,7 @@ import org.junit.Test; import static org.hamcrest.CoreMatchers.containsString; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; public class JApiCmpTaskTest { @Rule diff --git a/japicmp-maven-plugin/src/main/java/japicmp/maven/JApiCmpMojo.java b/japicmp-maven-plugin/src/main/java/japicmp/maven/JApiCmpMojo.java index cc146e6ea..0614ee561 100644 --- a/japicmp-maven-plugin/src/main/java/japicmp/maven/JApiCmpMojo.java +++ b/japicmp-maven-plugin/src/main/java/japicmp/maven/JApiCmpMojo.java @@ -11,6 +11,9 @@ import japicmp.model.JApiClass; import japicmp.model.JApiCompatibilityChangeType; import japicmp.model.JApiSemanticVersionLevel; +import japicmp.output.html.HtmlOutput; +import japicmp.output.html.HtmlOutputGenerator; +import japicmp.output.html.HtmlOutputGeneratorOptions; import japicmp.output.incompatible.IncompatibleErrorOutput; import japicmp.output.semver.SemverOut; import japicmp.output.stdout.StdoutOutputGenerator; @@ -42,6 +45,10 @@ import java.io.IOException; import java.io.OutputStreamWriter; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.*; import java.util.jar.JarFile; import java.util.regex.Matcher; @@ -112,7 +119,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { executeWithParameters(pluginParameters, mavenParameters); } - Optional executeWithParameters(PluginParameters pluginParameters, MavenParameters mavenParameters) throws MojoFailureException, MojoExecutionException { + Optional executeWithParameters(PluginParameters pluginParameters, MavenParameters mavenParameters) throws MojoFailureException, MojoExecutionException { if (pluginParameters.getSkipParam()) { getLog().info("Skipping execution because parameter 'skip' was set to true."); return Optional.absent(); @@ -138,16 +145,30 @@ Optional executeWithParameters(PluginParameters pluginParameters, Mav PostAnalysisScriptExecutor postAnalysisScriptExecutor = new PostAnalysisScriptExecutor(); jApiClasses = postAnalysisScriptExecutor.apply(pluginParameters.getParameterParam(), jApiClasses, getLog()); File jApiCmpBuildDir = createJapiCmpBaseDir(pluginParameters); - generateDiffOutput(mavenParameters, pluginParameters, options, jApiClasses, jApiCmpBuildDir); - XmlOutput xmlOutput = generateXmlOutput(jApiClasses, jApiCmpBuildDir, options, mavenParameters, pluginParameters); - if (pluginParameters.isWriteToFiles()) { - List filesWritten = XmlOutputGenerator.writeToFiles(options, xmlOutput); - for (File file : filesWritten) { - getLog().info("Written file '" + file.getAbsolutePath() + "'."); + SemverOut semverOut = new SemverOut(options, jApiClasses); + String semanticVersioningInformation = semverOut.generate(); + generateDiffOutput(mavenParameters, pluginParameters, options, jApiClasses, jApiCmpBuildDir, semanticVersioningInformation); + if (!skipXmlReport(pluginParameters)) { + XmlOutput xmlOutput = generateXmlOutput(jApiClasses, jApiCmpBuildDir, options, mavenParameters, pluginParameters, semanticVersioningInformation); + if (pluginParameters.isWriteToFiles()) { + List filesWritten = XmlOutputGenerator.writeToFiles(options, xmlOutput); + for (File file : filesWritten) { + getLog().info("Written file '" + file.getAbsolutePath() + "'."); + } + } + } + Optional retVal = Optional.absent(); + if (!skipHtmlReport(pluginParameters)) { + HtmlOutput htmlOutput = generateHtmlOutput(jApiClasses, jApiCmpBuildDir, options, mavenParameters, pluginParameters, semanticVersioningInformation); + retVal = Optional.of(htmlOutput); + if (pluginParameters.isWriteToFiles() && options.getHtmlOutputFile().isPresent()) { + Path path = Paths.get(options.getHtmlOutputFile().get()); + Files.write(path, htmlOutput.getHtml().getBytes(StandardCharsets.UTF_8)); + getLog().info("Written file '" + path + "'."); } } breakBuildIfNecessary(jApiClasses, pluginParameters.getParameterParam(), options, jarArchiveComparator); - return Optional.of(xmlOutput); + return retVal; } catch (IOException e) { throw new MojoFailureException(String.format("Failed to construct output directory: %s", e.getMessage()), e); } @@ -197,19 +218,19 @@ private enum ConfigurationVersion { } private static DefaultArtifact createDefaultArtifact(MavenProject mavenProject, String version) { - org.apache.maven.artifact.Artifact artifact = mavenProject.getArtifact(); - return createDefaultArtifact(artifact.getGroupId(), artifact.getArtifactId(), artifact.getClassifier(), artifact.getType(), version); + org.apache.maven.artifact.Artifact artifact = mavenProject.getArtifact(); + return createDefaultArtifact(artifact.getGroupId(), artifact.getArtifactId(), artifact.getClassifier(), artifact.getType(), version); } - private static DefaultArtifact createDefaultArtifact(String groupId, String artifactId, String classifier, String type, String version) { - String mappedType = type; - if("bundle".equals(type) || "ejb".equals(type)) { - mappedType ="jar"; - } - DefaultArtifact artifactVersion = new DefaultArtifact(groupId, artifactId, classifier, mappedType, - version); - return artifactVersion; - } + private static DefaultArtifact createDefaultArtifact(String groupId, String artifactId, String classifier, String type, String version) { + String mappedType = type; + if ("bundle".equals(type) || "ejb".equals(type)) { + mappedType = "jar"; + } + DefaultArtifact artifactVersion = new DefaultArtifact(groupId, artifactId, classifier, mappedType, + version); + return artifactVersion; + } private Artifact getComparisonArtifact(final MavenParameters mavenParameters, final PluginParameters pluginParameters, final ConfigurationVersion configurationVersion) throws MojoFailureException, MojoExecutionException { @@ -223,7 +244,7 @@ private Artifact getComparisonArtifact(final MavenParameters mavenParameters, fi filterSnapshots(versions, pluginParameters); filterVersionPattern(versions, pluginParameters); if (!versions.isEmpty()) { - DefaultArtifact artifactVersion = createDefaultArtifact(mavenProject, versions.get(versions.size()-1).toString()); + DefaultArtifact artifactVersion = createDefaultArtifact(mavenProject, versions.get(versions.size() - 1).toString()); ArtifactRequest artifactRequest = new ArtifactRequest(artifactVersion, mavenParameters.getRemoteRepos(), null); ArtifactResult artifactResult = mavenParameters.getRepoSystem().resolveArtifact(mavenParameters.getRepoSession(), artifactRequest); processArtifactResult(artifactVersion, artifactResult, pluginParameters, configurationVersion); @@ -231,11 +252,11 @@ private Artifact getComparisonArtifact(final MavenParameters mavenParameters, fi } else { if (ignoreMissingOldVersion(pluginParameters, configurationVersion)) { getLog().warn("Ignoring missing old artifact version: " + - artifactVersionRange.getGroupId() + ":" + artifactVersionRange.getArtifactId()); + artifactVersionRange.getGroupId() + ":" + artifactVersionRange.getArtifactId()); return null; } else { throw new MojoFailureException("Could not find previous version for artifact: " + artifactVersionRange.getGroupId() + ":" - + artifactVersionRange.getArtifactId()); + + artifactVersionRange.getArtifactId()); } } } catch (final VersionRangeResolutionException | ArtifactResolutionException e) { @@ -252,7 +273,7 @@ private void processArtifactResult(DefaultArtifact artifactVersion, ArtifactResu getLog().debug(exception.getMessage(), exception); } } - if (artifactResult.isMissing()){ + if (artifactResult.isMissing()) { if (ignoreMissingArtifact(pluginParameters, configurationVersion)) { getLog().warn("Ignoring missing artifact: " + artifactResult.getArtifact()); } else { @@ -539,7 +560,8 @@ private File createJapiCmpBaseDir(PluginParameters pluginParameters) throws Mojo } } - private void generateDiffOutput(MavenParameters mavenParameters, PluginParameters pluginParameters, Options options, List jApiClasses, File jApiCmpBuildDir) throws IOException, MojoFailureException { + private void generateDiffOutput(MavenParameters mavenParameters, PluginParameters pluginParameters, Options options, + List jApiClasses, File jApiCmpBuildDir, String semanticVersioningInformation) throws IOException, MojoFailureException { boolean skipDiffReport = false; if (pluginParameters.getParameterParam() != null) { skipDiffReport = pluginParameters.getParameterParam().isSkipDiffReport(); @@ -547,31 +569,41 @@ private void generateDiffOutput(MavenParameters mavenParameters, PluginParameter if (!skipDiffReport) { StdoutOutputGenerator stdoutOutputGenerator = new StdoutOutputGenerator(options, jApiClasses); String diffOutput = stdoutOutputGenerator.generate(); + diffOutput += "\nSemantic versioning suggestion: " + semanticVersioningInformation; File output = new File(jApiCmpBuildDir.getCanonicalPath() + File.separator + createFilename(mavenParameters) + ".diff"); writeToFile(diffOutput, output); } } - private XmlOutput generateXmlOutput(List jApiClasses, File jApiCmpBuildDir, Options options, MavenParameters mavenParameters, PluginParameters pluginParameters) throws IOException { + private XmlOutput generateXmlOutput(List jApiClasses, File jApiCmpBuildDir, Options options, MavenParameters mavenParameters, + PluginParameters pluginParameters, String semanticVersioningInformation) throws IOException { String filename = createFilename(mavenParameters); - if (!skipXmlReport(pluginParameters)) { - options.setXmlOutputFile(Optional.of(jApiCmpBuildDir.getCanonicalPath() + File.separator + filename + ".xml")); - } - if (!skipHtmlReport(pluginParameters)) { - options.setHtmlOutputFile(Optional.of(jApiCmpBuildDir.getCanonicalPath() + File.separator + filename + ".html")); - } - SemverOut semverOut = new SemverOut(options, jApiClasses); + options.setXmlOutputFile(Optional.of(jApiCmpBuildDir.getCanonicalPath() + File.separator + filename + ".xml")); XmlOutputGeneratorOptions xmlOutputGeneratorOptions = new XmlOutputGeneratorOptions(); xmlOutputGeneratorOptions.setCreateSchemaFile(true); - xmlOutputGeneratorOptions.setSemanticVersioningInformation(semverOut.generate()); + xmlOutputGeneratorOptions.setSemanticVersioningInformation(semanticVersioningInformation); if (pluginParameters.getParameterParam() != null) { String optionalTitle = pluginParameters.getParameterParam().getHtmlTitle(); - xmlOutputGeneratorOptions.setTitle(optionalTitle!=null ?optionalTitle :options.getDifferenceDescription()); + xmlOutputGeneratorOptions.setTitle(optionalTitle != null ? optionalTitle : options.getDifferenceDescription()); } XmlOutputGenerator xmlGenerator = new XmlOutputGenerator(jApiClasses, options, xmlOutputGeneratorOptions); return xmlGenerator.generate(); } + private HtmlOutput generateHtmlOutput(List jApiClasses, File jApiCmpBuildDir, Options options, MavenParameters mavenParameters, + PluginParameters pluginParameters, String semanticVersioningInformation) throws IOException { + String filename = createFilename(mavenParameters); + options.setHtmlOutputFile(Optional.of(jApiCmpBuildDir.getCanonicalPath() + File.separator + filename + ".html")); + HtmlOutputGeneratorOptions htmlOutputGeneratorOptions = new HtmlOutputGeneratorOptions(); + htmlOutputGeneratorOptions.setSemanticVersioningInformation(semanticVersioningInformation); + if (pluginParameters.getParameterParam() != null) { + String title = pluginParameters.getParameterParam().getHtmlTitle(); + htmlOutputGeneratorOptions.setTitle(title != null ? title : options.getDifferenceDescription()); + } + HtmlOutputGenerator htmlOutputGenerator = new HtmlOutputGenerator(jApiClasses, options, htmlOutputGeneratorOptions); + return htmlOutputGenerator.generate(); + } + private boolean skipHtmlReport(PluginParameters pluginParameters) { boolean skipReport = false; if (pluginParameters.getParameterParam() != null) { @@ -682,8 +714,8 @@ private Set getCompileArtifacts(final MavenProject mavenProject) { for (org.apache.maven.artifact.Artifact dep : projectDependencies) { if (dep.getArtifactHandler().isAddedToClasspath()) { if (org.apache.maven.artifact.Artifact.SCOPE_COMPILE.equals(dep.getScope()) - || org.apache.maven.artifact.Artifact.SCOPE_PROVIDED.equals(dep.getScope()) - || org.apache.maven.artifact.Artifact.SCOPE_SYSTEM.equals(dep.getScope())) { + || org.apache.maven.artifact.Artifact.SCOPE_PROVIDED.equals(dep.getScope()) + || org.apache.maven.artifact.Artifact.SCOPE_SYSTEM.equals(dep.getScope())) { result.add(RepositoryUtils.toArtifact(dep)); } } diff --git a/japicmp-maven-plugin/src/main/java/japicmp/maven/JApiCmpReport.java b/japicmp-maven-plugin/src/main/java/japicmp/maven/JApiCmpReport.java index f0406bdfc..33b934f92 100644 --- a/japicmp-maven-plugin/src/main/java/japicmp/maven/JApiCmpReport.java +++ b/japicmp-maven-plugin/src/main/java/japicmp/maven/JApiCmpReport.java @@ -1,7 +1,7 @@ package japicmp.maven; import japicmp.config.Options; -import japicmp.output.xml.XmlOutput; +import japicmp.output.html.HtmlOutput; import japicmp.util.Optional; import org.apache.maven.artifact.repository.ArtifactRepository; import org.apache.maven.doxia.sink.Sink; @@ -17,7 +17,6 @@ import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.repository.RemoteRepository; -import java.io.ByteArrayOutputStream; import java.io.File; import java.util.List; import java.util.Locale; @@ -72,29 +71,12 @@ protected void executeReport(Locale locale) throws MavenReportException { getLog().info("japicmp module set to skip"); return; } - Optional xmlOutputOptional = mojo.executeWithParameters(this.pluginParameters, this.mavenParameters); - if (xmlOutputOptional.isPresent()) { - XmlOutput xmlOutput = xmlOutputOptional.get(); - if (xmlOutput.getHtmlOutputStream().isPresent()) { - ByteArrayOutputStream htmlOutputStream = xmlOutput.getHtmlOutputStream().get(); - String htmlString = htmlOutputStream.toString("UTF-8"); - htmlString = htmlString.replaceAll("", ""); - htmlString = htmlString.replaceAll("", ""); - htmlString = htmlString.replaceAll("", ""); - htmlString = htmlString.replaceAll("[^<]*", ""); - htmlString = htmlString.replaceAll("]*>", ""); - Sink sink = getSink(); - String htmlTitle = getHtmlTitle(); - if (htmlTitle != null) { - sink.head(); - sink.title(); - sink.text(pluginParameters.getParameterParam().getHtmlTitle()); - sink.title_(); - sink.head_(); - } - sink.rawText(htmlString); - sink.close(); - } + Optional htmlOutputOptional = mojo.executeWithParameters(this.pluginParameters, this.mavenParameters); + if (htmlOutputOptional.isPresent()) { + HtmlOutput htmlOutput = htmlOutputOptional.get(); + String htmlString = htmlOutput.getHtml(); + htmlString = replaceHtmlTags(htmlString); + writeToSink(htmlString); } } catch (Exception e) { String msg = "Failed to generate report: " + e.getMessage(); @@ -105,16 +87,42 @@ protected void executeReport(Locale locale) throws MavenReportException { } } + private void writeToSink(String htmlString) { + Sink sink = getSink(); + try { + String htmlTitle = getHtmlTitle(); + if (htmlTitle != null) { + sink.head(); + sink.title(); + sink.text(pluginParameters.getParameterParam().getHtmlTitle()); + sink.title_(); + sink.head_(); + } + sink.rawText(htmlString); + } finally { + sink.close(); + } + } + + private static String replaceHtmlTags(String html) { + html = html.replaceAll("", ""); + html = html.replaceAll("", ""); + html = html.replaceAll("", ""); + html = html.replaceAll("[^<]*", ""); + html = html.replaceAll("]*>", ""); + return html; + } + private JApiCmpMojo getMojo() { if (this.mojo != null) { return this.mojo; } this.mojo = new JApiCmpMojo(); this.mavenParameters = new MavenParameters(this.artifactRepositories, - this.mavenProject, this.mojoExecution, this.versionRangeWithProjectVersion, this.repoSystem, this.repoSession, - this.remoteRepos); + this.mavenProject, this.mojoExecution, this.versionRangeWithProjectVersion, this.repoSystem, this.repoSession, + this.remoteRepos); this.pluginParameters = new PluginParameters(this.skip, this.newVersion, this.oldVersion, this.parameter, this.dependencies, Optional.absent(), Optional.of( - this.outputDirectory), false, this.oldVersions, this.newVersions, this.oldClassPathDependencies, this.newClassPathDependencies); + this.outputDirectory), false, this.oldVersions, this.newVersions, this.oldClassPathDependencies, this.newClassPathDependencies); return this.mojo; } @@ -165,6 +173,6 @@ public String getDescription(Locale locale) { private boolean isPomModuleNeedingSkip() { return this.pluginParameters.getParameterParam().getSkipPomModules() - && "pom".equalsIgnoreCase(this.mavenProject.getArtifact().getType()); + && "pom".equalsIgnoreCase(this.mavenProject.getArtifact().getType()); } } diff --git a/japicmp-testbase/japicmp-test-maven-plugin/src/test/java/japicmp/test/ITClassFileFormatVersion.java b/japicmp-testbase/japicmp-test-maven-plugin/src/test/java/japicmp/test/ITClassFileFormatVersion.java index 003d9cc8d..4da3a7335 100644 --- a/japicmp-testbase/japicmp-test-maven-plugin/src/test/java/japicmp/test/ITClassFileFormatVersion.java +++ b/japicmp-testbase/japicmp-test-maven-plugin/src/test/java/japicmp/test/ITClassFileFormatVersion.java @@ -8,14 +8,14 @@ import org.junit.Test; import java.io.IOException; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; public class ITClassFileFormatVersion { @@ -25,14 +25,14 @@ public void testClassFileFormatVersionIsPresent() throws IOException { if (!Files.exists(htmlPath)) { return; //in JDK 1.7 case } - Document document = Jsoup.parse(htmlPath.toFile(), Charset.forName("UTF-8").toString()); + Document document = Jsoup.parse(htmlPath.toFile(), StandardCharsets.UTF_8.toString()); Elements classFileFormatElements = document.select(".class_fileFormatVersion"); assertThat(classFileFormatElements.isEmpty(), is(false)); Elements tdCells = classFileFormatElements.select("table > tbody > tr > td"); assertThat(tdCells.isEmpty(), is(false)); for (Element element : tdCells) { String text = element.text(); - if (!"MODIFIED".equals(text) && !"50.0".equals(text) && !"52.0".equals(text)) { + if (!"MODIFIED (!)".equals(text) && !"50.0".equals(text) && !"52.0".equals(text)) { Assert.fail("text of HTML element does not equal 'MODIFIED' or 50.0 or 52.0: " + text); } } @@ -45,7 +45,7 @@ public void testStoutDiffClassFileFormatVersionIsPresent() throws IOException { return; //in JDK 1.7 case } assertThat(Files.exists(path), is(true)); - List lines = Files.readAllLines(path, Charset.forName("UTF-8")); + List lines = Files.readAllLines(path, StandardCharsets.UTF_8); boolean found = false; for (String line : lines) { if (line.contains("***! CLASS FILE FORMAT VERSION: 52.0 <- 50.0")) { diff --git a/japicmp-testbase/japicmp-test-maven-plugin/src/test/java/japicmp/test/ITStylesheet.java b/japicmp-testbase/japicmp-test-maven-plugin/src/test/java/japicmp/test/ITStylesheet.java index 136f11112..3342b024a 100644 --- a/japicmp-testbase/japicmp-test-maven-plugin/src/test/java/japicmp/test/ITStylesheet.java +++ b/japicmp-testbase/japicmp-test-maven-plugin/src/test/java/japicmp/test/ITStylesheet.java @@ -3,16 +3,16 @@ import org.junit.Test; import java.io.IOException; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsCollectionContaining.hasItem; import static org.hamcrest.core.IsNot.not; -import static org.junit.Assert.assertThat; public class ITStylesheet { @@ -20,8 +20,8 @@ public class ITStylesheet { public void testStylesheetIsUsed() throws IOException { Path htmlPath = Paths.get(System.getProperty("user.dir"), "target", "japicmp", "single-version.html"); assertThat(Files.exists(htmlPath), is(true)); - List htmlLines = Files.readAllLines(htmlPath, Charset.forName("UTF-8")); - List cssLines = Files.readAllLines(Paths.get(System.getProperty("user.dir"), "src", "main", "resources", "css", "stylesheet.css"), Charset.forName("UTF-8")); + List htmlLines = Files.readAllLines(htmlPath, StandardCharsets.UTF_8); + List cssLines = Files.readAllLines(Paths.get(System.getProperty("user.dir"), "src", "main", "resources", "css", "stylesheet.css"), StandardCharsets.UTF_8); assertThat(htmlLines.size(), not(is(0))); assertThat(cssLines.size(), not(is(0))); for (String cssLine : cssLines) { diff --git a/japicmp/src/main/java/japicmp/cmp/JApiCmpArchive.java b/japicmp/src/main/java/japicmp/cmp/JApiCmpArchive.java index d1652ae1d..91c4df65f 100644 --- a/japicmp/src/main/java/japicmp/cmp/JApiCmpArchive.java +++ b/japicmp/src/main/java/japicmp/cmp/JApiCmpArchive.java @@ -8,16 +8,18 @@ public class JApiCmpArchive { private File file; private byte[] bytes; private final Version version; + private String name; public JApiCmpArchive(File file, String version) { this.file = file; this.version = new Version(version); } - public JApiCmpArchive(byte[] bytes, String version) { + public JApiCmpArchive(byte[] bytes, String version, String name) { this.bytes = bytes; this.version = new Version(version); - } + this.name = name; + } public File getFile() { return file; @@ -31,9 +33,13 @@ public byte[] getBytes() { return bytes; } + public String getName() { + return name; + } + @Override public String toString() { - final StringBuilder sb = new StringBuilder("JApiCmpArchive{"); + final StringBuffer sb = new StringBuffer("JApiCmpArchive{"); sb.append("file=").append(file); sb.append(", bytes="); if (bytes == null) sb.append("null"); @@ -44,6 +50,7 @@ public String toString() { sb.append(']'); } sb.append(", version=").append(version); + sb.append(", name='").append(name).append('\''); sb.append('}'); return sb.toString(); } diff --git a/japicmp/src/main/java/japicmp/config/Options.java b/japicmp/src/main/java/japicmp/config/Options.java index 8db5fe6ce..f8842ad9f 100644 --- a/japicmp/src/main/java/japicmp/config/Options.java +++ b/japicmp/src/main/java/japicmp/config/Options.java @@ -1,22 +1,15 @@ package japicmp.config; import com.google.common.base.Joiner; -import japicmp.util.Optional; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; import japicmp.cli.CliParser; import japicmp.cli.JApiCli; import japicmp.cmp.JApiCmpArchive; import japicmp.exception.JApiCmpException; -import japicmp.filter.AnnotationBehaviorFilter; -import japicmp.filter.AnnotationClassFilter; -import japicmp.filter.AnnotationFieldFilter; -import japicmp.filter.Filter; -import japicmp.filter.JavaDocLikeClassFilter; -import japicmp.filter.JavadocLikeBehaviorFilter; -import japicmp.filter.JavadocLikeFieldFilter; -import japicmp.filter.JavadocLikePackageFilter; +import japicmp.filter.*; import japicmp.model.AccessModifier; +import japicmp.util.Optional; import java.io.File; import java.io.IOException; @@ -329,9 +322,17 @@ private List toPathList(List archives) { List paths = new ArrayList<>(archives.size()); for (JApiCmpArchive archive : archives) { if (this.reportOnlyFilename) { - paths.add(archive.getFile().getName()); + if (archive.getFile() != null) { + paths.add(archive.getFile().getName()); + } else { + paths.add(archive.getName()); + } } else { - paths.add(archive.getFile().getAbsolutePath()); + if (archive.getFile() != null) { + paths.add(archive.getFile().getAbsolutePath()); + } else { + paths.add(archive.getName()); + } } } return paths; @@ -349,36 +350,32 @@ private List toVersionList(List archives) { } public String joinOldArchives() { - Joiner joiner = Joiner.on(";"); - String join = joiner.join(toPathList(oldArchives)); - if (join.trim().length() == 0) { + String join = Joiner.on(";").join(toPathList(oldArchives)); + if (join.trim().isEmpty()) { return N_A; } return join; } public String joinNewArchives() { - Joiner joiner = Joiner.on(";"); - String join = joiner.join(toPathList(newArchives)); - if (join.trim().length() == 0) { + String join = Joiner.on(";").join(toPathList(newArchives)); + if (join.trim().isEmpty()) { return N_A; } return join; } public String joinOldVersions() { - Joiner joiner = Joiner.on(";"); - String join = joiner.join(toVersionList(oldArchives)); - if (join.trim().length() == 0) { + String join = Joiner.on(";").join(toVersionList(oldArchives)); + if (join.trim().isEmpty()) { return N_A; } return join; } public String joinNewVersions() { - Joiner joiner = Joiner.on(";"); - String join = joiner.join(toVersionList(newArchives)); - if (join.trim().length() == 0) { + String join = Joiner.on(";").join(toVersionList(newArchives)); + if (join.trim().isEmpty()) { return N_A; } return join; diff --git a/japicmp/src/main/java/japicmp/output/html/HtmlOutputGenerator.java b/japicmp/src/main/java/japicmp/output/html/HtmlOutputGenerator.java index 9d91bdff2..8f5aeb470 100644 --- a/japicmp/src/main/java/japicmp/output/html/HtmlOutputGenerator.java +++ b/japicmp/src/main/java/japicmp/output/html/HtmlOutputGenerator.java @@ -20,10 +20,12 @@ public class HtmlOutputGenerator extends OutputGenerator { private final HtmlOutputGeneratorOptions htmlOutputGeneratorOptions; private final static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); + private final TemplateEngine templateEngine; public HtmlOutputGenerator(List jApiClasses, Options options, HtmlOutputGeneratorOptions htmlOutputGeneratorOptions) { super(options, jApiClasses); this.htmlOutputGeneratorOptions = htmlOutputGeneratorOptions; + this.templateEngine = new TemplateEngine(); } @Override @@ -34,11 +36,11 @@ public HtmlOutput generate() { sb.append("\n"); sb.append("\n"); sb.append("").append(getTitle()).append("\n"); - sb.append("\n"); + sb.append("\n"); sb.append("\n"); sb.append("\n"); sb.append("").append(getTitle()).append("\n"); - sb.append("
"); + sb.append("
\n"); metaInformation(sb); warningMissingClasses(sb); toc(sb); @@ -51,7 +53,7 @@ public HtmlOutput generate() { private void classes(StringBuilder sb) { sb.append(jApiClasses.stream() - .map(jApiClass -> loadAndFillTemplate("/html/class-entry.html", mapOf( + .map(jApiClass -> templateEngine.loadAndFillTemplate("/html/class-entry.html", mapOf( "fullyQualifiedName", jApiClass.getFullyQualifiedName(), "outputChangeStatus", outputChangeStatus(jApiClass), "javaObjectSerializationCompatible", javaObjectSerializationCompatible(jApiClass), @@ -59,7 +61,7 @@ private void classes(StringBuilder sb) { "classType", classType(jApiClass), "compatibilityChanges", compatibilityChanges(jApiClass, false), "classFileFormatVersion", classFileFormatVersion(jApiClass), - "genericTemplates", genericTemplates(jApiClass), + "genericTemplates", genericTemplates(jApiClass, false), "superclass", superclass(jApiClass), "interfaces", interfaces(jApiClass), "serialVersionUid", serialVersionUid(jApiClass), @@ -73,7 +75,7 @@ private void classes(StringBuilder sb) { private String methods(JApiClass jApiClass) { if (!jApiClass.getMethods().isEmpty()) { - return loadAndFillTemplate("/html/methods.html", mapOf( + return templateEngine.loadAndFillTemplate("/html/methods.html", mapOf( "tbody", methodsTBody(jApiClass.getMethods()) )); } @@ -86,13 +88,13 @@ private String methodsTBody(List methods) { .map(method -> "\n" + "" + outputChangeStatus(method) + "\n" + "" + modifiers(method) + "\n" + - "" + genericTemplates(method) + "\n" + + "" + genericTemplates(method, true) + "\n" + "" + returnType(method) + "\n" + "" + method.getName() + "(" + parameters(method) + ")" + annotations(method.getAnnotations()) + "\n" + "" + exceptions(method) + "\n" + "" + compatibilityChanges(method, true) + "\n" + "" + - loadAndFillTemplate("/html/line-numbers.html", mapOf( + templateEngine.loadAndFillTemplate("/html/line-numbers.html", mapOf( "oldLineNumber", method.getOldLineNumberAsString(), "newLineNumber", method.getNewLineNumberAsString())) + "\n" + "\n") @@ -120,7 +122,7 @@ private String returnTypeValue(JApiReturnType returnType) { private String constructors(JApiClass jApiClass) { if (!jApiClass.getConstructors().isEmpty()) { - return loadAndFillTemplate("/html/constructors.html", mapOf( + return templateEngine.loadAndFillTemplate("/html/constructors.html", mapOf( "tbody", constructors(jApiClass.getConstructors()) )); } @@ -132,12 +134,12 @@ private String constructors(List constructors) { .map(constructor -> "\n" + "" + outputChangeStatus(constructor) + "\n" + "" + modifiers(constructor) + "\n" + - "" + genericTemplates(constructor) + "\n" + + "" + genericTemplates(constructor, true) + "\n" + "" + constructor.getName() + "(" + parameters(constructor) + ")" + annotations(constructor.getAnnotations()) + "\n" + "" + exceptions(constructor) + "\n" + "" + compatibilityChanges(constructor, true) + "\n" + "" + - loadAndFillTemplate("/html/line-numbers.html", mapOf( + templateEngine.loadAndFillTemplate("/html/line-numbers.html", mapOf( "oldLineNumber", constructor.getOldLineNumberAsString(), "newLineNumber", constructor.getNewLineNumberAsString())) + "\n" + "\n") @@ -146,7 +148,7 @@ private String constructors(List constructors) { private String exceptions(JApiBehavior jApiBehavior) { if (!jApiBehavior.getExceptions().isEmpty()) { - return loadAndFillTemplate("/html/exceptions.html", mapOf( + return templateEngine.loadAndFillTemplate("/html/exceptions.html", mapOf( "tbody", exceptionsTBody(jApiBehavior.getExceptions()) )); } @@ -174,7 +176,7 @@ private String parameters(JApiBehavior jApiBehavior) { private String fields(JApiClass jApiClass) { if (!jApiClass.getFields().isEmpty()) { - return loadAndFillTemplate("/html/fields.html", mapOf( + return templateEngine.loadAndFillTemplate("/html/fields.html", mapOf( "tbody", fields(jApiClass.getFields()) )); } @@ -216,7 +218,7 @@ private String typeValue(JApiField field) { private String annotations(List annotations) { if (!annotations.isEmpty()) { - return loadAndFillTemplate("/html/annotations.html", mapOf( + return templateEngine.loadAndFillTemplate("/html/annotations.html", mapOf( "tbody", annotationsTBody(annotations) )); } @@ -236,7 +238,7 @@ private String annotationsTBody(List annotations) { private String elements(JApiAnnotation annotation) { if (!annotation.getElements().isEmpty()) { - return loadAndFillTemplate("/html/annotation-elements.html", mapOf( + return templateEngine.loadAndFillTemplate("/html/annotation-elements.html", mapOf( "tbody", annotationElements(annotation.getElements()) )); } else { @@ -282,7 +284,7 @@ private String values(JApiAnnotationElementValue value) { private String serialVersionUid(JApiClass jApiClass) { if (jApiClass.getSerialVersionUid().isSerializableOld() || jApiClass.getSerialVersionUid().isSerializableNew()) { - return loadAndFillTemplate("/html/serial-version-uid.html", mapOf( + return templateEngine.loadAndFillTemplate("/html/serial-version-uid.html", mapOf( "tbody", serialVersionUidTBody(jApiClass.getSerialVersionUid()) )); } @@ -306,7 +308,7 @@ private String serialVersionUidTBody(JApiSerialVersionUid serialVersionUid) { private String interfaces(JApiClass jApiClass) { if (!jApiClass.getInterfaces().isEmpty()) { - return loadAndFillTemplate("/html/interfaces.html", mapOf( + return templateEngine.loadAndFillTemplate("/html/interfaces.html", mapOf( "tbody", interfacesTBody(jApiClass.getInterfaces()) )); } @@ -332,7 +334,7 @@ private String superclass(JApiClass jApiClass) { (superclass.getChangeStatus() == JApiChangeStatus.UNCHANGED && !superclass.getSuperclassOld().equalsIgnoreCase("java.lang.Object")) ) ) { - return loadAndFillTemplate("/html/superclass.html", mapOf( + return templateEngine.loadAndFillTemplate("/html/superclass.html", mapOf( "tbody", superclassTBody(jApiClass.getSuperclass()) )); } @@ -360,15 +362,15 @@ private String superclassName(JApiSuperclass superclass) { return ""; } - private String genericTemplates(JApiHasGenericTemplates jApiHasGenericTemplates) { + private String genericTemplates(JApiHasGenericTemplates jApiHasGenericTemplates, boolean withNA) { List genericTemplates = jApiHasGenericTemplates.getGenericTemplates(); if (!genericTemplates.isEmpty()) { return "Generic Templates:\n" + - loadAndFillTemplate("/html/generic-templates.html", mapOf( + templateEngine.loadAndFillTemplate("/html/generic-templates.html", mapOf( "tbody", genericTemplatesTBody(genericTemplates) )); } - return ""; + return withNA ? "n.a." : ""; } private String genericTemplatesTBody(List genericTemplates) { @@ -402,16 +404,16 @@ private String genericParameterTypesRecursive(JApiGenericType jApiGenericType) { return ""; } - private String genericParameterWithWildcard(JApiGenericType jApiGenericType1) { - switch (jApiGenericType1.getGenericWildCard()) { + private String genericParameterWithWildcard(JApiGenericType jApiGenericType) { + switch (jApiGenericType.getGenericWildCard()) { case NONE: - return jApiGenericType1.getType(); + return jApiGenericType.getType(); case EXTENDS: - return "? extends " + jApiGenericType1.getType(); + return "? extends " + jApiGenericType.getType(); case SUPER: - return "? super " + jApiGenericType1.getType(); + return "? super " + jApiGenericType.getType(); case UNBOUNDED: - return "? " + jApiGenericType1.getType(); + return "?"; } return ""; } @@ -448,7 +450,7 @@ private String genericTypes(String header, List genericTypes) { private String classFileFormatVersion(JApiClass jApiClass) { if (jApiClass.getClassFileFormatVersion().getChangeStatus() == JApiChangeStatus.MODIFIED) { - return loadAndFillTemplate("/html/class-file-format-version.html", mapOf( + return templateEngine.loadAndFillTemplate("/html/class-file-format-version.html", mapOf( "tbody", classFileFormatVersionTBody(jApiClass) )); } @@ -473,7 +475,7 @@ private String classFileFormatVersionString(int majorVersion, int minorVersion) private String compatibilityChanges(JApiCompatibility jApiClass, boolean withNA) { if (!jApiClass.getCompatibilityChanges().isEmpty()) { - return loadAndFillTemplate("/html/compatibility-changes.html", mapOf( + return templateEngine.loadAndFillTemplate("/html/compatibility-changes.html", mapOf( "tbody", jApiClass.getCompatibilityChanges().stream() .map(this::compatibilityChange) .collect(Collectors.joining()) @@ -553,7 +555,7 @@ private void toc(StringBuilder sb) { sb.append("Classes\n"); sb.append("\n"); sb.append("\n"); - sb.append(loadAndFillTemplate("/html/toc.html", mapOf( + sb.append(templateEngine.loadAndFillTemplate("/html/toc.html", mapOf( "tbody", tocEntries() ))); } @@ -561,7 +563,7 @@ private void toc(StringBuilder sb) { private String tocEntries() { return jApiClasses.stream() - .map(jApiClass -> loadAndFillTemplate("/html/toc-entry.html", mapOf( + .map(jApiClass -> templateEngine.loadAndFillTemplate("/html/toc-entry.html", mapOf( "outputChangeStatus", outputChangeStatus(jApiClass), "fullyQualifiedName", jApiClass.getFullyQualifiedName() ))) @@ -607,13 +609,13 @@ private String getStyle() { throw new JApiCmpException(JApiCmpException.Reason.IoException, "Failed to load stylesheet: " + e.getMessage(), e); } } else { - styleSheet = loadTemplate("/style.css"); + styleSheet = templateEngine.loadTemplate("/style.css"); } return styleSheet; } private void metaInformation(StringBuilder sb) { - sb.append(loadAndFillTemplate("/html/meta-information.html", mapOf( + sb.append(templateEngine.loadAndFillTemplate("/html/meta-information.html", mapOf( "oldJar", options.joinOldArchives(), "newJar", options.joinNewArchives(), "newJar", options.joinNewArchives(), @@ -637,22 +639,6 @@ private Map mapOf(String... args) { return map; } - private String loadAndFillTemplate(String path, Map params) { - String template = loadTemplate(path); - for (Map.Entry entry : params.entrySet()) { - template = template.replace("${" + entry.getKey() + "}", entry.getValue()); - } - return template; - } - - private String loadTemplate(String path) { - InputStream resourceAsStream = HtmlOutputGenerator.class.getResourceAsStream(path); - if (resourceAsStream == null) { - throw new JApiCmpException(JApiCmpException.Reason.ResourceNotFound, "Failed to load: " + path); - } - return Streams.asString(resourceAsStream); - } - private String getTitle() { if (this.htmlOutputGeneratorOptions.getTitle().isPresent()) { return this.htmlOutputGeneratorOptions.getTitle().get(); diff --git a/japicmp/src/main/java/japicmp/output/html/TemplateEngine.java b/japicmp/src/main/java/japicmp/output/html/TemplateEngine.java new file mode 100644 index 000000000..4277e2f39 --- /dev/null +++ b/japicmp/src/main/java/japicmp/output/html/TemplateEngine.java @@ -0,0 +1,33 @@ +package japicmp.output.html; + +import japicmp.exception.JApiCmpException; +import japicmp.util.Streams; + +import java.io.InputStream; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class TemplateEngine { + private static final Map templateCache = new ConcurrentHashMap<>(); + + public String loadAndFillTemplate(String path, Map params) { + String template = loadTemplate(path); + for (Map.Entry entry : params.entrySet()) { + template = template.replace("${" + entry.getKey() + "}", entry.getValue()); + } + return template; + } + + public String loadTemplate(String path) { + String template = templateCache.get(path); + if (template == null) { + InputStream resourceAsStream = HtmlOutputGenerator.class.getResourceAsStream(path); + if (resourceAsStream == null) { + throw new JApiCmpException(JApiCmpException.Reason.ResourceNotFound, "Failed to load: " + path); + } + template = Streams.asString(resourceAsStream); + templateCache.put(path, template); + } + return template; + } +} diff --git a/japicmp/src/main/java/japicmp/output/xml/XmlOutput.java b/japicmp/src/main/java/japicmp/output/xml/XmlOutput.java index 832045630..37d71695f 100644 --- a/japicmp/src/main/java/japicmp/output/xml/XmlOutput.java +++ b/japicmp/src/main/java/japicmp/output/xml/XmlOutput.java @@ -1,13 +1,12 @@ package japicmp.output.xml; -import japicmp.util.Optional; import japicmp.output.xml.model.JApiCmpXmlRoot; +import japicmp.util.Optional; import java.io.ByteArrayOutputStream; public class XmlOutput implements AutoCloseable { private Optional xmlOutputStream = Optional.absent(); - private Optional htmlOutputStream = Optional.absent(); private japicmp.output.xml.model.JApiCmpXmlRoot JApiCmpXmlRoot; public Optional getXmlOutputStream() { @@ -18,22 +17,11 @@ public void setXmlOutputStream(Optional xmlOutputStream) this.xmlOutputStream = xmlOutputStream; } - public Optional getHtmlOutputStream() { - return htmlOutputStream; - } - - public void setHtmlOutputStream(Optional htmlOutputStream) { - this.htmlOutputStream = htmlOutputStream; - } - @Override public void close() throws Exception { if (this.xmlOutputStream.isPresent()) { this.xmlOutputStream.get().close(); } - if (this.htmlOutputStream.isPresent()) { - this.htmlOutputStream.get().close(); - } } public void setJApiCmpXmlRoot(JApiCmpXmlRoot JApiCmpXmlRoot) { From f5119707239c3824658933b2992fb4f34c03b4a6 Mon Sep 17 00:00:00 2001 From: Martin Mois Date: Wed, 6 Mar 2024 22:02:04 +0100 Subject: [PATCH 6/7] removed html.xslt --- japicmp/src/main/resources/html.xslt | 1094 -------------------------- 1 file changed, 1094 deletions(-) delete mode 100644 japicmp/src/main/resources/html.xslt diff --git a/japicmp/src/main/resources/html.xslt b/japicmp/src/main/resources/html.xslt deleted file mode 100644 index bd1ac0246..000000000 --- a/japicmp/src/main/resources/html.xslt +++ /dev/null @@ -1,1094 +0,0 @@ - - - - - - - - - - - <xsl:value-of select="@title"/> - - - - - - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Old: - -
New: - -
Created: - -
Access modifier filter: - -
Only modifications: - -
Only binary incompatible modifications: - -
Ignore missing classes: - -
Includes: - -
Excludes: - -
Semantic Versioning: - -
- -
- - WARNING: You are using the option '--ignore-missing-classes', i.e. superclasses and - interfaces that could not be found on the classpath are ignored. Hence changes - caused by these superclasses and interfaces are not reflected in the output. - -
-
-
- - - - -
- - -
- Classes: - - - - - - - - - - - - -
StatusFully Qualified Name
-
-
- Binary incompatible changes are marked with (!) while source incompatible changes are marked with (*). -
-
- - - -
-
- - - - - - - - - # - - - - - - - -
-
- -
- - - - - - - -   - - top -
- -
- Compatibility Changes: - - - - - - - - - - - - - -
Change
-
-
- -
- class File Format Version: - - - - - - - - - - - -
StatusOld VersionNew Version
-
-
-
- - Generic Templates: - - -
-
- - Superclass: - - - - - - - - - - - -
StatusSuperclassCompatibility Changes
-
-
-
- - Interfaces: - - - - - - - - - - - - - -
StatusInterfaceCompatibility Changes
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - -
Serializabledefault serialVersionUIDserialVersionUID in class
Old - - modified - - - - - modified - - - - - modified - - -
New - - modified - - - - - modified - - - - - modified - - -
-
-
-
- - Fields: - - - - - - - - - - - - - - - -
StatusModifierTypeFieldCompatibility Changes:
-
-
-
- - Constructors: - - - - - - - - - - - - - - - - - -
StatusModifierGeneric TemplatesConstructorExceptionsCompatibility Changes:Line Number
-
-
-
- - Methods: - - - - - - - - - - - - - - - - - - -
StatusModifierGeneric TemplatesTypeMethodExceptionsCompatibility Changes:Line Number
-
-
- -
-
-
- - - -
- Annotations: - - - - - - - - - - - - - -
Status:Fully Qualified Name:Elements:
-
-
-
- - - - - - . - - - n.a. - - - . - - - n.a. - - - - - - - - - - - - - - - - - - - (<- ) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - () - - - - - - - - - - - - - - - - - - - - - -
Old fileNew file
- - - -
- - -
- - - - - - - - - - - - - - - - - () - - - - - - - - - - - - - - - - - - - - - -
Old fileNew file
- - - -
- - -
- - - - - - - - - - - - - - - - - - -
Status:Name:
- - - -
-
- n.a. -
- - - - - - - - - - - - - - - - - - - - - - - -
Status:Name:Old element values:New element values:
-
- n.a. - - -
- - - - - - - - - - - - - , - - - @() - - - {} - - - . - - - - - - - - - - , - - - @() - - - {} - - - . - - - - - - - - - - - - - - , - - - @() - - - {} - - - . - - - - - - - - - - - - , - - - - modified method_parameter - - - unchanged method_parameter - - - new method_parameter - - - removed method_parameter - - - - - - - - - - - -  (!) -  (*) - - - - - - - modified - - - unchanged - - - new - - - removed - - - - - - - - - - - - - modified modifier - - - unchanged modifier - - - new modifier - - - removed modifier - - - - - - - - -  (<-  - - - - - )  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - not_final - - - not_transient - - - not_volatile - - - not_static - - - not_abstract - - - not_bridge - - - not_synthetic - - - package_protected - - - -   - - - - - - - - - - - - - - - - - - - -
Change
-
- n.a. -
- - - - - - modified modifier - - - unchanged modifier - - - new modifier - - - removed modifier - - - - -  (<- ) - - - - - - - - - - - - - (!) - - - (!) - - - - - - - - - modified method_return_type - - - unchanged method_return_type - - - new method_return_type - - - removed method_return_type - - - - -  (<- ) - - - - - - - - - - - - - - - - - - - modified - - - unchanged - - - new - - - removed - - - - -  (<- ) - - - - - - - - - - - - - - - - - -   - new (Serializable compatible)  - removed (Serializable incompatible(!): - - - - - - -
- - - - modified method_parameter - - - unchanged method_parameter - - - <..> - -
- - - - - - - - - - - - - - - - - -
New: - - -
Old: - - -
-
-
-
-
- - - - < - - , - - - - > - - - - - - - - - - ? extends - - - ? super - - - ? - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Change StatusNameOld TypeNew TypeGenerics
- - - - - - - - & - - - - - - - - & - - - - - -
-
- - n.a. - -
-
From b0296f3d23cd5b190c60a2f24aaefab19b773e60 Mon Sep 17 00:00:00 2001 From: Martin Mois Date: Wed, 6 Mar 2024 22:02:24 +0100 Subject: [PATCH 7/7] StringBuilder instead of StringBuffer --- japicmp/src/main/java/japicmp/cmp/JApiCmpArchive.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/japicmp/src/main/java/japicmp/cmp/JApiCmpArchive.java b/japicmp/src/main/java/japicmp/cmp/JApiCmpArchive.java index 91c4df65f..55e8f88c5 100644 --- a/japicmp/src/main/java/japicmp/cmp/JApiCmpArchive.java +++ b/japicmp/src/main/java/japicmp/cmp/JApiCmpArchive.java @@ -39,7 +39,7 @@ public String getName() { @Override public String toString() { - final StringBuffer sb = new StringBuffer("JApiCmpArchive{"); + StringBuilder sb = new StringBuilder("JApiCmpArchive{"); sb.append("file=").append(file); sb.append(", bytes="); if (bytes == null) sb.append("null");