From 118f74cc4fc24b53d048f0466ec253082ee737c9 Mon Sep 17 00:00:00 2001 From: Abel Salgado Romero Date: Thu, 25 Jan 2024 00:35:38 +0100 Subject: [PATCH] WIP --- asciidoctor-maven-plugin/pom.xml | 6 + .../asciidoctor/maven/AsciidoctorMojo.java | 51 +++++--- .../maven/AsciidoctorZipMojoTest.java | 9 +- .../maven/ParametersInitializer.java | 89 +++++++++++++ .../maven/ParametersInitializerTest.java | 123 ++++++++++++++++++ .../java/org/asciidoctor/maven/TestUtils.java | 18 ++- 6 files changed, 270 insertions(+), 26 deletions(-) create mode 100644 asciidoctor-maven-plugin/src/test/java/org/asciidoctor/maven/ParametersInitializer.java create mode 100644 asciidoctor-maven-plugin/src/test/java/org/asciidoctor/maven/ParametersInitializerTest.java diff --git a/asciidoctor-maven-plugin/pom.xml b/asciidoctor-maven-plugin/pom.xml index ee4200cc..81f247ce 100644 --- a/asciidoctor-maven-plugin/pom.xml +++ b/asciidoctor-maven-plugin/pom.xml @@ -62,6 +62,12 @@ netty-codec-http 4.1.106.Final + + net.bytebuddy + byte-buddy + 1.14.11 + test + diff --git a/asciidoctor-maven-plugin/src/main/java/org/asciidoctor/maven/AsciidoctorMojo.java b/asciidoctor-maven-plugin/src/main/java/org/asciidoctor/maven/AsciidoctorMojo.java index 0bd4b88a..c80bc91f 100644 --- a/asciidoctor-maven-plugin/src/main/java/org/asciidoctor/maven/AsciidoctorMojo.java +++ b/asciidoctor-maven-plugin/src/main/java/org/asciidoctor/maven/AsciidoctorMojo.java @@ -7,7 +7,12 @@ import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.project.MavenProject; -import org.asciidoctor.*; +import org.asciidoctor.Asciidoctor; +import org.asciidoctor.Attributes; +import org.asciidoctor.AttributesBuilder; +import org.asciidoctor.Options; +import org.asciidoctor.OptionsBuilder; +import org.asciidoctor.SafeMode; import org.asciidoctor.jruby.AsciidoctorJRuby; import org.asciidoctor.jruby.internal.JRubyRuntimeContext; import org.asciidoctor.maven.commons.AsciidoctorHelper; @@ -29,10 +34,18 @@ import java.io.File; import java.io.IOException; import java.nio.file.Path; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; import java.util.logging.Logger; import static org.asciidoctor.maven.commons.StringUtils.isBlank; +import static org.asciidoctor.maven.commons.StringUtils.isNotBlank; import static org.asciidoctor.maven.process.SourceDirectoryFinder.DEFAULT_SOURCE_DIR; @@ -52,10 +65,10 @@ public class AsciidoctorMojo extends AbstractMojo { protected File outputFile; @Parameter(property = AsciidoctorMaven.PREFIX + "preserveDirectories", defaultValue = "false") - protected boolean preserveDirectories = false; + protected boolean preserveDirectories; @Parameter(property = AsciidoctorMaven.PREFIX + "relativeBaseDir", defaultValue = "false") - protected boolean relativeBaseDir = false; + protected boolean relativeBaseDir; @Parameter(property = AsciidoctorMaven.PREFIX + "projectDirectory", defaultValue = "${basedir}") protected File projectDirectory; @@ -66,8 +79,8 @@ public class AsciidoctorMojo extends AbstractMojo { @Parameter(property = AsciidoctorMaven.PREFIX + "baseDir") protected File baseDir; - @Parameter(property = AsciidoctorMaven.PREFIX + "skip") - protected boolean skip = false; + @Parameter(property = AsciidoctorMaven.PREFIX + "skip", defaultValue = "false") + protected boolean skip; @Parameter(property = AsciidoctorMaven.PREFIX + "gemPath") protected String gemPath; @@ -79,10 +92,10 @@ public class AsciidoctorMojo extends AbstractMojo { protected Map attributes = new HashMap<>(); @Parameter(property = AsciidoctorMaven.PREFIX + Options.ATTRIBUTES) - protected String attributesChain = ""; + protected String attributesChain; @Parameter(property = AsciidoctorMaven.PREFIX + Options.BACKEND, defaultValue = "html5") - protected String backend = "html5"; + protected String backend; @Parameter(property = AsciidoctorMaven.PREFIX + Options.DOCTYPE) protected String doctype; @@ -91,7 +104,7 @@ public class AsciidoctorMojo extends AbstractMojo { protected String eruby; @Parameter(property = AsciidoctorMaven.PREFIX + "standalone", defaultValue = "true") - protected boolean standalone = true; + protected boolean standalone; @Parameter(property = AsciidoctorMaven.PREFIX + "templateDirs") protected List templateDirs = new ArrayList<>(); @@ -99,8 +112,8 @@ public class AsciidoctorMojo extends AbstractMojo { @Parameter(property = AsciidoctorMaven.PREFIX + "templateEngine") protected String templateEngine; - @Parameter(property = AsciidoctorMaven.PREFIX + "templateCache") - protected boolean templateCache = true; + @Parameter(property = AsciidoctorMaven.PREFIX + "templateCache", defaultValue = "true") + protected boolean templateCache; @Parameter(property = AsciidoctorMaven.PREFIX + "sourceDocumentName") protected String sourceDocumentName; @@ -108,24 +121,24 @@ public class AsciidoctorMojo extends AbstractMojo { @Parameter(property = AsciidoctorMaven.PREFIX + "sourceDocumentExtensions") protected List sourceDocumentExtensions = new ArrayList<>(); - @Parameter(property = AsciidoctorMaven.PREFIX + "sourcemap") - protected boolean sourcemap = false; + @Parameter(property = AsciidoctorMaven.PREFIX + "sourcemap", defaultValue = "false") + protected boolean sourcemap; - @Parameter(property = AsciidoctorMaven.PREFIX + "catalogAssets") - protected boolean catalogAssets = false; + @Parameter(property = AsciidoctorMaven.PREFIX + "catalogAssets", defaultValue = "false") + protected boolean catalogAssets; @Parameter protected List extensions = new ArrayList<>(); @Parameter(property = AsciidoctorMaven.PREFIX + "embedAssets", defaultValue = "false") - protected boolean embedAssets = false; + protected boolean embedAssets; - // List of resources to copy to the output directory (e.g., images, css). By default everything is copied + // List of resources to copy to the output directory (e.g., images, css). By default, everything is copied @Parameter protected List resources; @Parameter(property = AsciidoctorMaven.PREFIX + "verbose", defaultValue = "false") - protected boolean enableVerbose = false; + protected boolean enableVerbose; @Parameter private LogHandler logHandler = new LogHandler(); @@ -455,7 +468,7 @@ protected AttributesBuilder createAttributesBuilder(AsciidoctorMojo configuratio AsciidoctorHelper.addProperties(mavenProject.getProperties(), attributesBuilder); AsciidoctorHelper.addAttributes(configuration.getAttributes(), attributesBuilder); - if (!configuration.getAttributesChain().isEmpty()) { + if (isNotBlank(configuration.getAttributesChain())) { getLog().info("Attributes: " + attributesChain); attributesBuilder.arguments(attributesChain); } diff --git a/asciidoctor-maven-plugin/src/test/java/org/asciidoctor/maven/AsciidoctorZipMojoTest.java b/asciidoctor-maven-plugin/src/test/java/org/asciidoctor/maven/AsciidoctorZipMojoTest.java index eaeadac7..db60562f 100644 --- a/asciidoctor-maven-plugin/src/test/java/org/asciidoctor/maven/AsciidoctorZipMojoTest.java +++ b/asciidoctor-maven-plugin/src/test/java/org/asciidoctor/maven/AsciidoctorZipMojoTest.java @@ -41,11 +41,10 @@ void should_create_simple_zip() throws IOException, MojoFailureException, MojoEx "= Title\n\ntest", UTF_8); AsciidoctorZipMojo mojo = mockAsciidoctorZipMojo(); - mojo.backend = "html"; mojo.sourceDirectory = srcDir; mojo.outputDirectory = outputDir; mojo.zipDestination = zip; - mojo.zip = true; + mojo.attach = false; mojo.execute(); // then: a zip is created @@ -72,13 +71,12 @@ void should_replicate_source_structure_in_zip_standard_paths() throws MojoFailur // when AsciidoctorZipMojo mojo = mockAsciidoctorZipMojo(); - mojo.backend = "html5"; mojo.sourceDirectory = srcDir; mojo.outputDirectory = outputDir; mojo.preserveDirectories = true; mojo.relativeBaseDir = true; mojo.zipDestination = zip; - mojo.zip = true; + mojo.attach = false; mojo.execute(); // then @@ -118,11 +116,10 @@ void should_not_replicate_source_structure_in_zip_standard_paths() throws IOExce // when AsciidoctorZipMojo mojo = mockAsciidoctorZipMojo(); - mojo.backend = "html5"; mojo.sourceDirectory = srcDir; mojo.outputDirectory = outputDir; mojo.zipDestination = zip; - mojo.zip = true; + mojo.attach = false; mojo.execute(); // then diff --git a/asciidoctor-maven-plugin/src/test/java/org/asciidoctor/maven/ParametersInitializer.java b/asciidoctor-maven-plugin/src/test/java/org/asciidoctor/maven/ParametersInitializer.java new file mode 100644 index 00000000..b868a514 --- /dev/null +++ b/asciidoctor-maven-plugin/src/test/java/org/asciidoctor/maven/ParametersInitializer.java @@ -0,0 +1,89 @@ +package org.asciidoctor.maven; + +import net.bytebuddy.description.annotation.AnnotationDescription; +import net.bytebuddy.description.annotation.AnnotationValue; +import net.bytebuddy.description.field.FieldDescription; +import net.bytebuddy.description.field.FieldList; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.pool.TypePool; +import org.apache.maven.plugins.annotations.Parameter; + +import static org.asciidoctor.maven.commons.StringUtils.isBlank; +import static org.codehaus.plexus.util.ReflectionUtils.setVariableValueInObject; + +/** + * + */ +class ParametersInitializer { + + private static final ClassLoader CLASS_LOADER = ParametersInitializer.class.getClassLoader(); + + /** + * Returns instance of input class with fields initialized according to its + * respective {@link org.apache.maven.plugins.annotations.Parameter}. + */ + public T initialize(T instance) { + try { + // Use ByteBuddy because annotations is Class retention, not Runtime + TypePool typePool = TypePool.Default.of(CLASS_LOADER); + TypeDescription typeDescription = typePool.describe(instance.getClass().getName()).resolve(); + + initParameterFields(instance, typeDescription); + return instance; + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + private void initParameterFields(T instance, TypeDescription typeDescription) throws IllegalAccessException { + + FieldList declaredFields = typeDescription.getDeclaredFields(); + + for (FieldDescription field : declaredFields) { + String value = getAnnotationByType(field); + if (value != null) { + if (field.getType().getTypeName().equals(String.class.getName())) { + if (value.length() > 0 && !value.startsWith("$")) { + // TODO support Maven variable: pass Map ? + setVariableValueInObject(instance, field.getName(), value); + } + } + if (field.getType().getTypeName().equals("boolean")) { + // false is already the default + // TODO for PR, the booleans default should appear in XML plugin descriptor now + if (value.equals("true")) { + setVariableValueInObject(instance, field.getName(), Boolean.TRUE); + } else if (!value.equals("false")) { + throw new RuntimeException("Invalid boolean default: not-a-boolean"); + } + } + // TODO + // if (field.getType().getTypeName().equals(File.class.getName())) { + } + } + + TypeDescription superClass = typeDescription.getSuperClass().asErasure(); + + if (hasParent(superClass)) { + initParameterFields(instance, superClass); + } + } + + private boolean hasParent(TypeDescription superClass) { + return superClass != null && !superClass.getTypeName().equals(Object.class.getName()); + } + + // Make MojoReader + private String getAnnotationByType(FieldDescription field) { + for (AnnotationDescription declaredAnnotation : field.getDeclaredAnnotations()) { + String annotationTypeName = declaredAnnotation.getAnnotationType().getName(); + if (annotationTypeName.equals(Parameter.class.getCanonicalName())) { + AnnotationValue defaultValue = declaredAnnotation.getValue("defaultValue"); + String stringValue = defaultValue.toString(); + stringValue = stringValue.substring(1, stringValue.length() - 1).trim(); + return isBlank(stringValue) ? null : stringValue; + } + } + return null; + } +} diff --git a/asciidoctor-maven-plugin/src/test/java/org/asciidoctor/maven/ParametersInitializerTest.java b/asciidoctor-maven-plugin/src/test/java/org/asciidoctor/maven/ParametersInitializerTest.java new file mode 100644 index 00000000..92ba43c1 --- /dev/null +++ b/asciidoctor-maven-plugin/src/test/java/org/asciidoctor/maven/ParametersInitializerTest.java @@ -0,0 +1,123 @@ +package org.asciidoctor.maven; + +import org.apache.maven.plugins.annotations.Parameter; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; + +class ParametersInitializerTest { + + private final ParametersInitializer initializer = new ParametersInitializer(); + + @Test + void should_return_same_instance() { + final var instance = new Simple(); + var actual = initializer.initialize(instance); + assertThat(actual).isEqualTo(instance); + } + + @Nested + class ShouldInitialize { + + @Test + void string_with_default_value() { + final var instance = new StringExampleMojo(); + var initialized = initializer.initialize(instance); + assertThat(initialized.defaultValue).isEqualTo("a-value"); + } + + @Test + void boolean_with_default_value() { + final var instance = new BooleanExampleMojo(); + var initialized = initializer.initialize(instance); + assertThat(initialized.defaultValue).isTrue(); + } + + @Test + void properties_in_class_and_parent() { + final var instance = new SubclassExampleMojo(); + var initialized = initializer.initialize(instance); + assertThat(initialized.getDefaultValue()).isEqualTo("a-value"); + assertThat(initialized.getNonDefaultValue()).isNull(); + assertThat(initialized.anotherValue).isEqualTo("from-subclass"); + } + } + + @Nested + class ShouldNotInitialize { + + @Test + void string_without_default_value() { + final var instance = new StringExampleMojo(); + var initialized = initializer.initialize(instance); + assertThat(initialized.nonDefaultValue).isNull(); + } + + @Test + void boolean_without_default_value() { + final var instance = new BooleanExampleMojo(); + var initialized = initializer.initialize(instance); + assertThat(initialized.nonDefaultValue).isFalse(); + } + } + + @Nested + class ShouldFail { + @Test + void boolean_with_invalid_value() { + final var instance = new FailingExampleMojo(); + Throwable t = catchThrowable(() -> initializer.initialize(instance)); + + assertThat(t).isInstanceOf(RuntimeException.class) + .hasMessage("Invalid boolean default: not-a-boolean"); + } + } + + + class Simple { + + Simple() { + } + + } + + class StringExampleMojo { + + @Parameter(defaultValue = "a-value") + private String defaultValue; + + @Parameter + private String nonDefaultValue; + + public String getDefaultValue() { + return defaultValue; + } + + public String getNonDefaultValue() { + return nonDefaultValue; + } + } + + class BooleanExampleMojo { + + @Parameter(defaultValue = "true") + private boolean defaultValue; + + @Parameter + private boolean nonDefaultValue; + } + + class FailingExampleMojo { + + @Parameter(defaultValue = "not-a-boolean") + private boolean invalidValue; + } + + class SubclassExampleMojo extends StringExampleMojo { + + @Parameter(defaultValue = "from-subclass") + private String anotherValue; + } +} diff --git a/asciidoctor-maven-plugin/src/test/java/org/asciidoctor/maven/TestUtils.java b/asciidoctor-maven-plugin/src/test/java/org/asciidoctor/maven/TestUtils.java index 49379fd9..05e6683d 100644 --- a/asciidoctor-maven-plugin/src/test/java/org/asciidoctor/maven/TestUtils.java +++ b/asciidoctor-maven-plugin/src/test/java/org/asciidoctor/maven/TestUtils.java @@ -1,8 +1,16 @@ package org.asciidoctor.maven; import lombok.SneakyThrows; +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.ClassFileVersion; +import net.bytebuddy.description.annotation.AnnotationDescription; +import net.bytebuddy.description.annotation.AnnotationValue; +import net.bytebuddy.description.field.FieldDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.pool.TypePool; import org.apache.commons.io.IOUtils; import org.apache.maven.plugin.logging.SystemStreamLog; +import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.project.MavenProject; import org.asciidoctor.maven.log.LogHandler; import org.asciidoctor.maven.model.Resource; @@ -12,17 +20,24 @@ import java.io.FileOutputStream; import java.io.FileReader; import java.nio.charset.StandardCharsets; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Properties; import java.util.stream.Collectors; import static java.util.Collections.singletonList; import static org.asciidoctor.maven.process.SourceDocumentFinder.STANDARD_FILE_EXTENSIONS_PATTERN; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatPath; import static org.codehaus.plexus.util.ReflectionUtils.setVariableValueInObject; import static org.mockito.Mockito.when; public class TestUtils { + private static final ParametersInitializer parametersInitializer = new ParametersInitializer(); + @SneakyThrows public static AsciidoctorRefreshMojo newFakeRefreshMojo() { return mockAsciidoctorMojo(AsciidoctorRefreshMojo.class, null, null); @@ -65,6 +80,7 @@ private static T mockAsciidoctorMojo(Class clazz, Map mav } final AsciidoctorMojo mojo = (AsciidoctorMojo) clazz.getConstructor(new Class[]{}).newInstance(); + parametersInitializer.initialize(mojo); setVariableValueInObject(mojo, "log", new SystemStreamLog()); mojo.project = mavenProject; if (logHandler != null)