start implement feature to use annotation as meta-annotation
Use internal cache to avoid a cycle between annotations.
Collect all name annotations and register extensions under all names.
MODIFY
asciidoctorj-core/src/main/java/org/asciidoctor/jruby/internal/JavaExtensionRegistryImpl.java
asciidoctorj-core/src/main/java/org/asciidoctor/jruby/internal/JavaExtensionRegistryImpl.java
@@ -22,6 +22,11 @@ import org.jruby.Ruby;
import org.jruby.RubyClass;
import org.jruby.RubyModule;
+import java.lang.annotation.Annotation;
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
public class JavaExtensionRegistryImpl implements JavaExtensionRegistry {
private final JRubyAsciidoctor asciidoctor;
@@ -192,8 +197,9 @@ public class JavaExtensionRegistryImpl implements JavaExtensionRegistry {
@Override
public JavaExtensionRegistry block(Class<? extends BlockProcessor> blockProcessor) {
- String name = getName(blockProcessor);
+ getNames(blockProcessor).forEach(name -> {
block(name, blockProcessor);
+ });
return this;
}
@@ -222,9 +228,10 @@ public class JavaExtensionRegistryImpl implements JavaExtensionRegistry {
@Override
public JavaExtensionRegistry blockMacro(Class<? extends BlockMacroProcessor> blockMacroProcessor) {
- String name = getName(blockMacroProcessor);
+ getNames(blockMacroProcessor).forEach(name -> {
RubyClass rubyClass = BlockMacroProcessorProxy.register(asciidoctor, blockMacroProcessor);
getAsciidoctorModule().callMethod("block_macro", rubyClass, rubyRuntime.newString(name));
+ });
return this;
}
@@ -290,9 +297,10 @@ public class JavaExtensionRegistryImpl implements JavaExtensionRegistry {
@Override
public JavaExtensionRegistry inlineMacro(Class<? extends InlineMacroProcessor> inlineMacroProcessor) {
- String name = getName(inlineMacroProcessor);
+ getNames(inlineMacroProcessor).forEach(name -> {
RubyClass rubyClass = InlineMacroProcessorProxy.register(asciidoctor, inlineMacroProcessor);
getAsciidoctorModule().callMethod("inline_macro", rubyClass, rubyRuntime.newString(name));
+ });
return this;
}
@@ -318,12 +326,29 @@ public class JavaExtensionRegistryImpl implements JavaExtensionRegistry {
}
}
- private String getName(Class<?> clazz) {
- Name nameAnnotation = clazz.getAnnotation(Name.class);
- if (nameAnnotation == null) {
+ private Set<Name> getNameAnnotations(Annotation[] annotations, Set<Annotation> visitedAnnotations) {
+ Set<Name> allNameAnnotations = new HashSet<>();
+ for (Annotation anno : annotations) {
+ if (visitedAnnotations.contains(anno)) {
+ continue;
+ }
+ if (anno.annotationType().isAssignableFrom(Name.class)) {
+ allNameAnnotations.add((Name) anno);
+ } else {
+ visitedAnnotations.add(anno);
+ allNameAnnotations.addAll(getNameAnnotations(anno.annotationType().getAnnotations(), visitedAnnotations));
+ }
+ }
+ return allNameAnnotations;
+ }
+
+ private Set<String> getNames(Class<?> clazz) {
+ Set<String> nameAnnotations = getNameAnnotations(clazz.getAnnotations(), new HashSet<Annotation>()).stream().map(Name::value).distinct().collect(Collectors.toSet());
+ if (nameAnnotations.isEmpty()) {
throw new IllegalArgumentException(clazz + " must be registered with a name or it must have a Name annotation!");
+ } else {
+ return nameAnnotations;
}
- return nameAnnotation.value();
}
private RubyModule getAsciidoctorModule() {

MODIFY
asciidoctorj-core/src/test/java/org/asciidoctor/extension/BlockMacroRegistrationTest.java
asciidoctorj-core/src/test/java/org/asciidoctor/extension/BlockMacroRegistrationTest.java
@@ -10,8 +10,11 @@ import org.jsoup.Jsoup;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.lang.annotation.*;
import java.util.Map;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static java.util.stream.Collectors.joining;
import static org.junit.Assert.assertEquals;
@@ -50,11 +53,29 @@ public class BlockMacroRegistrationTest {
}
}
+ @Documented
+ @Target(TYPE)
+ @Retention(RUNTIME)
+ @Name(MetaNameAnnotation.NAME)
+ public @interface MetaNameAnnotation {
+ String NAME = "annotated";
+ }
+
@Name(AnnotatedTestProcessor.NAME)
public static class AnnotatedTestProcessor extends AbstractTestProcessor {
public static final String NAME = "annotated";
}
+ @MetaNameAnnotation
+ public static class AnnotatedTestProcessorWithMetaNameAnnotation extends AbstractTestProcessor {
+ }
+
+ @MetaNameAnnotation
+ @Name(AnnotatedTestProcessorWithMultipleNameAnnotations.NAME)
+ public static class AnnotatedTestProcessorWithMultipleNameAnnotations extends AbstractTestProcessor {
+ public static final String NAME = "anotherName";
+ }
+
@Test
public void testRegisterNamedClassAsClass() {
asciidoctor.javaExtensionRegistry().blockMacro(AnnotatedTestProcessor.class);
@@ -62,6 +83,22 @@ public class BlockMacroRegistrationTest {
check("HELLO WORLD", result);
}
+ @Test
+ public void testRegisterClassWithMetaNameAnnotation() {
+ asciidoctor.javaExtensionRegistry().blockMacro(AnnotatedTestProcessorWithMetaNameAnnotation.class);
+ final String result = asciidoctor.convert(document(MetaNameAnnotation.NAME, "Hello World"), OptionsBuilder.options());
+ check("HELLO WORLD", result);
+ }
+
+ @Test
+ public void testRegisterClassWithMultipleNameAnnotations() {
+ asciidoctor.javaExtensionRegistry().blockMacro(AnnotatedTestProcessorWithMultipleNameAnnotations.class);
+ String result = asciidoctor.convert(document(MetaNameAnnotation.NAME, "Hello World"), OptionsBuilder.options());
+ check("HELLO WORLD", result);
+ result = asciidoctor.convert(document(AnnotatedTestProcessorWithMultipleNameAnnotations.NAME, "Hello World"), OptionsBuilder.options());
+ check("HELLO WORLD", result);
+ }
+
@Test
public void testRegisterNamedClassAsClassWithExplicitName() {
final String explicitblockname = "explicitblockname";
@@ -69,6 +106,7 @@ public class BlockMacroRegistrationTest {
final String result = asciidoctor.convert(document(explicitblockname, "Hello Explicit"), OptionsBuilder.options());
check("HELLO EXPLICIT", result);
}
+
@Test
public void testRegisterNamedClassAsInstance() {
asciidoctor.javaExtensionRegistry().blockMacro(new AnnotatedTestProcessor());

start implement feature to use @Name annotation as meta-annotation

Use internal cache to avoid a cycle between annotations.
Collect all name annotations and register extensions under all names.