diff --git a/README.adoc b/README.adoc index 5d5f88d2f..7f468a3f0 100644 --- a/README.adoc +++ b/README.adoc @@ -1380,6 +1380,30 @@ String content = asciidoctor.convertFile(new File( You can unregister all extensions by calling the `org.asciidoctor.Asciidoctor.unregisterAllExtensions()` method. +Additionally, since AsciidoctorJ 1.5.6.1 it is possible to unregister specific extensions when using the ExtensionGroup API. +This allows to register and unregister a group of extensions as a whole: + +[source,java] +---- +BlockProcessor extension1 = ... +Treeprocessor extension2 = ... +ExtensionGroup extensionGroup = + asciidoctor.createGroup() + .block(extension1) + .treeprocessor(extension2); + +asciidoctor.convert(...) // <1> + +extensionGroup.register(); +asciidoctor.convert(); // <2> + +extensionGroup.register(); +asciidoctor.convert(); // <3> +---- +<1> Convert a document without the extensions active because they were not registered yet. +<2> Convert with extensions active after registration. +<3> Convert without extensions active after the extensions were unregistered. + === Ruby extensions You can even register extensions written in Ruby using AsciidoctorJ. diff --git a/asciidoctorj-core/src/main/java/org/asciidoctor/Asciidoctor.java b/asciidoctorj-core/src/main/java/org/asciidoctor/Asciidoctor.java index 12f12c4f5..1d539b6db 100644 --- a/asciidoctorj-core/src/main/java/org/asciidoctor/Asciidoctor.java +++ b/asciidoctorj-core/src/main/java/org/asciidoctor/Asciidoctor.java @@ -13,6 +13,7 @@ import org.asciidoctor.ast.DocumentRuby; import org.asciidoctor.ast.StructuredDocument; import org.asciidoctor.converter.JavaConverterRegistry; +import org.asciidoctor.extension.ExtensionGroup; import org.asciidoctor.extension.JavaExtensionRegistry; import org.asciidoctor.extension.RubyExtensionRegistry; import org.asciidoctor.internal.JRubyAsciidoctor; @@ -633,12 +634,23 @@ String[] convertFiles(Collection asciidoctorFiles, * @return Converter Registry object. */ JavaConverterRegistry javaConverterRegistry(); - + + /** + * Creates an ExtensionGroup that can be used to register and unregister a group of extensions. + * @return + */ + ExtensionGroup createGroup(); + + /** + * Creates an ExtensionGroup that can be used to register and unregister a group of extensions. + * @return + */ + ExtensionGroup createGroup(String groupName); /** * Unregister all registered extensions. */ void unregisterAllExtensions(); - + /** * This method frees all resources consumed by asciidoctorJ module. Keep in mind that if this method is called, instance becomes unusable and you should create another instance. */ diff --git a/asciidoctorj-core/src/main/java/org/asciidoctor/extension/ExtensionGroup.java b/asciidoctorj-core/src/main/java/org/asciidoctor/extension/ExtensionGroup.java new file mode 100644 index 000000000..248c5fe57 --- /dev/null +++ b/asciidoctorj-core/src/main/java/org/asciidoctor/extension/ExtensionGroup.java @@ -0,0 +1,74 @@ +package org.asciidoctor.extension; + + +import java.io.InputStream; + +/** + * An ExtensionGroup allows to collectively register and unregister extensions. + * All extensions are registered lazily and are not effective before a call to {@link #register()}. + * + *

Example: + *

+ * ExtensionGroup group = asciidoctor.createGroup();
+ * group.block(myBlock).preprocessor(mypreprocessor);
+ *
+ * // Convert with extensions
+ * group.register();
+ * asciidoctor.convert(...);
+ * group.unregister();
+ *
+ * // Convert without extensions
+ * asciidoctor.convert(...);
+ * 

+ */ +public interface ExtensionGroup { + + public void register(); + + public void unregister(); + + public ExtensionGroup docinfoProcessor(Class docInfoProcessor); + public ExtensionGroup docinfoProcessor(DocinfoProcessor docInfoProcessor); + public ExtensionGroup docinfoProcessor(String docInfoProcessor); + + public ExtensionGroup preprocessor(Class preprocessor); + public ExtensionGroup preprocessor(Preprocessor preprocessor); + public ExtensionGroup preprocessor(String preprocessor); + + public ExtensionGroup postprocessor(String postprocessor); + public ExtensionGroup postprocessor(Class postprocessor); + public ExtensionGroup postprocessor(Postprocessor postprocesor); + + public ExtensionGroup includeProcessor(String includeProcessor); + public ExtensionGroup includeProcessor(Class includeProcessor); + public ExtensionGroup includeProcessor(IncludeProcessor includeProcessor); + + public ExtensionGroup treeprocessor(Treeprocessor treeprocessor); + public ExtensionGroup treeprocessor(Class treeProcessor); + public ExtensionGroup treeprocessor(String treeProcessor); + + public ExtensionGroup block(String blockName, String blockProcessor); + public ExtensionGroup block(String blockName, Class blockProcessor); + public ExtensionGroup block(BlockProcessor blockProcessor); + public ExtensionGroup block(String blockName, BlockProcessor blockProcessor); + + public ExtensionGroup blockMacro(String blockName, Class blockMacroProcessor); + public ExtensionGroup blockMacro(String blockName, String blockMacroProcessor); + public ExtensionGroup blockMacro(BlockMacroProcessor blockMacroProcessor); + + public ExtensionGroup inlineMacro(InlineMacroProcessor inlineMacroProcessor); + public ExtensionGroup inlineMacro(String blockName, Class inlineMacroProcessor); + public ExtensionGroup inlineMacro(String blockName, String inlineMacroProcessor); + + public ExtensionGroup requireRubyLibrary(String requiredLibrary); + public ExtensionGroup loadRubyClass(InputStream rubyClassStream); + + public ExtensionGroup rubyPreprocessor(String preprocessor); + public ExtensionGroup rubyPostprocessor(String postprocessor); + public ExtensionGroup rubyDocinfoProcessor(String docinfoProcessor); + public ExtensionGroup rubyIncludeProcessor(String includeProcessor); + public ExtensionGroup rubyTreeprocessor(String treeProcessor); + public ExtensionGroup rubyBlock(String blockName, String blockProcessor); + public ExtensionGroup rubyBlockMacro(String blockName, String blockMacroProcessor); + public ExtensionGroup rubyInlineMacro(String blockName, String inlineMacroProcessor); +} diff --git a/asciidoctorj-core/src/main/java/org/asciidoctor/extension/RubyExtensionRegistry.java b/asciidoctorj-core/src/main/java/org/asciidoctor/extension/RubyExtensionRegistry.java index 1b429ca51..69a1e19db 100644 --- a/asciidoctorj-core/src/main/java/org/asciidoctor/extension/RubyExtensionRegistry.java +++ b/asciidoctorj-core/src/main/java/org/asciidoctor/extension/RubyExtensionRegistry.java @@ -9,6 +9,7 @@ public class RubyExtensionRegistry { private AsciidoctorModule asciidoctorModule; + private Ruby rubyRuntime; public RubyExtensionRegistry(AsciidoctorModule asciidoctorModule, @@ -33,8 +34,8 @@ public RubyExtensionRegistry preprocessor(String preprocessor) { return this; } - public RubyExtensionRegistry postprocessor(String postprocesor) { - this.asciidoctorModule.postprocessor(postprocesor); + public RubyExtensionRegistry postprocessor(String postprocessor) { + this.asciidoctorModule.postprocessor(postprocessor); return this; } @@ -72,5 +73,4 @@ public RubyExtensionRegistry inlineMacro(String blockName, String inlineMacroPro inlineMacroProcessor, RubyUtils.toSymbol(rubyRuntime, blockName)); return this; } - } \ No newline at end of file diff --git a/asciidoctorj-core/src/main/java/org/asciidoctor/internal/AsciidoctorModule.java b/asciidoctorj-core/src/main/java/org/asciidoctor/internal/AsciidoctorModule.java index 7aa01e418..6a0399b42 100644 --- a/asciidoctorj-core/src/main/java/org/asciidoctor/internal/AsciidoctorModule.java +++ b/asciidoctorj-core/src/main/java/org/asciidoctor/internal/AsciidoctorModule.java @@ -13,38 +13,63 @@ public interface AsciidoctorModule { void preprocessor(String preprocessorClassName); + void preprocessor(String preprocessorClassName, String registrationName); void preprocessor(RubyClass preprocessorClassName); - void preprocessor(Preprocessor preprocessor); - + void preprocessor(RubyClass preprocessorClassName, String registrationName); + void preprocessor(Preprocessor preprocessor); + void preprocessor(Preprocessor preprocessor, String registrationName); + void postprocessor(String postprocessorClassName); + void postprocessor(String postprocessorClassName, String registrationName); void postprocessor(RubyClass postprocessorClassName); + void postprocessor(RubyClass postprocessorClassName, String registrationName); void postprocessor(Postprocessor postprocessor); - + void postprocessor(Postprocessor postprocessor, String registrationName); + void treeprocessor(String treeprocessor); + void treeprocessor(String treeprocessor, String registrationName); void treeprocessor(RubyClass treeprocessorClassName); + void treeprocessor(RubyClass treeprocessorClassName, String registrationName); void treeprocessor(Treeprocessor treeprocessorClassName); - + void treeprocessor(Treeprocessor treeprocessorClassName, String registrationName); + void include_processor(String includeProcessorClassName); + void include_processor(String includeProcessorClassName, String registrationName); void include_processor(RubyClass includeProcessorClassName); + void include_processor(RubyClass includeProcessorClassName, String registrationName); void include_processor(IncludeProcessor includeProcessor); - + void include_processor(IncludeProcessor includeProcessor, String registrationName); + void block_processor(String blockClassName, Object blockName); + void block_processor(String blockClassName, Object blockName, String registrationName); void block_processor(RubyClass blockClass, Object blockName); + void block_processor(RubyClass blockClass, Object blockName, String registrationName); void block_processor(BlockProcessor blockInstance, Object blockName); - + void block_processor(BlockProcessor blockInstance, Object blockName, String registrationName); + void block_macro(String blockMacroClassName, Object blockName); + void block_macro(String blockMacroClassName, Object blockName, String registrationName); void block_macro(Class blockMacroClass, Object blockName); + void block_macro(Class blockMacroClass, Object blockName, String registrationName); void block_macro(BlockMacroProcessor blockMacroInstance, Object blockName); - + void block_macro(BlockMacroProcessor blockMacroInstance, Object blockName, String registrationName); + void inline_macro(String blockClassName, Object blockSymbol); + void inline_macro(String blockClassName, Object blockSymbol, String registrationName); void inline_macro(RubyClass blockClassName, Object blockSymbol); + void inline_macro(RubyClass blockClassName, Object blockSymbol, String registrationName); void inline_macro(InlineMacroProcessor blockClassName, Object blockSymbol); - + void inline_macro(InlineMacroProcessor blockClassName, Object blockSymbol, String registrationName); + void docinfo_processor(String docInfoClassName); + void docinfo_processor(String docInfoClassName, String registrationName); void docinfo_processor(RubyClass docInfoClassName); + void docinfo_processor(RubyClass docInfoClassName, String registrationName); void docinfo_processor(DocinfoProcessor docInfoClassName); - + void docinfo_processor(DocinfoProcessor docInfoClassName, String registrationName); + void unregister_all_extensions(); + void unregister_extension(String groupName); Object convert(String content, Map options); Object convertFile(String filename, Map options); diff --git a/asciidoctorj-core/src/main/java/org/asciidoctor/internal/ExtensionGroupImpl.java b/asciidoctorj-core/src/main/java/org/asciidoctor/internal/ExtensionGroupImpl.java new file mode 100644 index 000000000..4d7586982 --- /dev/null +++ b/asciidoctorj-core/src/main/java/org/asciidoctor/internal/ExtensionGroupImpl.java @@ -0,0 +1,506 @@ +package org.asciidoctor.internal; + +import org.asciidoctor.extension.BlockMacroProcessor; +import org.asciidoctor.extension.BlockProcessor; +import org.asciidoctor.extension.DocinfoProcessor; +import org.asciidoctor.extension.ExtensionGroup; +import org.asciidoctor.extension.IncludeProcessor; +import org.asciidoctor.extension.InlineMacroProcessor; +import org.asciidoctor.extension.Postprocessor; +import org.asciidoctor.extension.Preprocessor; +import org.asciidoctor.extension.Treeprocessor; +import org.jruby.Ruby; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by robertpanzer on 21.07.17. + */ +public class ExtensionGroupImpl implements ExtensionGroup { + + private final JRubyAsciidoctor asciidoctor; + + private final Ruby rubyRuntime; + + private final AsciidoctorModule asciidoctorModule; + + private final String groupName; + + private final List registrations = new ArrayList(); + + public ExtensionGroupImpl(String groupName, JRubyAsciidoctor asciidoctor) { + this.groupName = groupName; + this.asciidoctor = asciidoctor; + this.rubyRuntime = asciidoctor.getRubyRuntime(); + asciidoctorModule = asciidoctor.getAsciidoctorModule(); + } + + @Override + public void register() { + for (Runnable r: registrations) { + r.run(); + } + } + + @Override + public void unregister() { + asciidoctorModule.unregister_extension(groupName); + } + + @Override + public ExtensionGroup docinfoProcessor(final Class docInfoProcessor) { + registrations.add(new Runnable() { + @Override + public void run() { + javaImport(rubyRuntime, docInfoProcessor); + asciidoctorModule.docinfo_processor(RubyUtils.toRubyClass(rubyRuntime, docInfoProcessor), groupName); + } + }); + return this; + } + + @Override + public ExtensionGroup docinfoProcessor(final DocinfoProcessor docInfoProcessor) { + registrations.add(new Runnable() { + @Override + public void run() { + javaImport(rubyRuntime, docInfoProcessor.getClass()); + asciidoctorModule.docinfo_processor(docInfoProcessor, groupName); + } + }); + return this; + } + + @Override + public ExtensionGroup docinfoProcessor(final String docInfoProcessor) { + registrations.add(new Runnable() { + @Override + public void run() { + javaImport(rubyRuntime, docInfoProcessor); + asciidoctorModule.docinfo_processor(getClassName(docInfoProcessor), groupName); + + } + }); + return this; + } + + @Override + public ExtensionGroup preprocessor(final Class preprocessor) { + registrations.add(new Runnable() { + @Override + public void run() { + javaImport(rubyRuntime, preprocessor); + asciidoctorModule.preprocessor(RubyUtils.toRubyClass(rubyRuntime, preprocessor), groupName); + } + }); + return this; + } + + @Override + public ExtensionGroup preprocessor(final Preprocessor preprocessor) { + registrations.add(new Runnable() { + @Override + public void run() { + javaImport(rubyRuntime, preprocessor.getClass()); + asciidoctorModule.preprocessor(preprocessor, groupName); + } + }); + return this; + } + + @Override + public ExtensionGroup preprocessor(final String preprocessor) { + registrations.add(new Runnable() { + @Override + public void run() { + javaImport(rubyRuntime, preprocessor); + asciidoctorModule.preprocessor(getClassName(preprocessor), groupName); + } + }); + return this; + } + + @Override + public ExtensionGroup postprocessor(final String postprocessor) { + registrations.add(new Runnable() { + @Override + public void run() { + javaImport(rubyRuntime, postprocessor); + asciidoctorModule.postprocessor(getClassName(postprocessor), groupName); + } + }); + return this; + } + + @Override + public ExtensionGroup postprocessor(final Class postprocessor) { + registrations.add(new Runnable() { + @Override + public void run() { + javaImport(rubyRuntime, postprocessor); + asciidoctorModule.postprocessor(RubyUtils.toRubyClass(rubyRuntime, postprocessor), groupName); + } + }); + return this; + } + + @Override + public ExtensionGroup postprocessor(final Postprocessor postprocesor) { + registrations.add(new Runnable() { + @Override + public void run() { + javaImport(rubyRuntime, postprocesor.getClass()); + asciidoctorModule.postprocessor(postprocesor, groupName); + } + }); + return this; + } + + @Override + public ExtensionGroup includeProcessor(final String includeProcessor) { + registrations.add(new Runnable() { + @Override + public void run() { + javaImport(rubyRuntime, includeProcessor); + asciidoctorModule.include_processor(getClassName(includeProcessor), groupName); + } + }); + return this; + } + + @Override + public ExtensionGroup includeProcessor(final Class includeProcessor) { + registrations.add(new Runnable() { + @Override + public void run() { + javaImport(rubyRuntime, includeProcessor); + asciidoctorModule.include_processor(RubyUtils.toRubyClass(rubyRuntime, includeProcessor), groupName); + } + }); + return this; + } + + @Override + public ExtensionGroup includeProcessor(final IncludeProcessor includeProcessor) { + registrations.add(new Runnable() { + @Override + public void run() { + String importLine = getImportLine(includeProcessor.getClass()); + javaImport(rubyRuntime, importLine); + asciidoctorModule.include_processor(includeProcessor, groupName); + } + }); + return this; + } + + @Override + public ExtensionGroup treeprocessor(final Treeprocessor treeprocessor) { + registrations.add(new Runnable() { + @Override + public void run() { + javaImport(rubyRuntime, treeprocessor.getClass()); + asciidoctorModule.treeprocessor(treeprocessor, groupName); + } + }); + return this; + } + + @Override + public ExtensionGroup treeprocessor(final Class treeProcessor) { + registrations.add(new Runnable() { + @Override + public void run() { + javaImport(rubyRuntime, treeProcessor); + asciidoctorModule.treeprocessor(RubyUtils.toRubyClass(rubyRuntime, treeProcessor), groupName); + } + }); + return this; + } + + @Override + public ExtensionGroup treeprocessor(final String treeProcessor) { + registrations.add(new Runnable() { + @Override + public void run() { + javaImport(rubyRuntime, treeProcessor); + asciidoctorModule.treeprocessor(getClassName(treeProcessor), groupName); + } + }); + return this; + } + + @Override + public ExtensionGroup block(final String blockName, final String blockProcessor) { + registrations.add(new Runnable() { + @Override + public void run() { + javaImport(rubyRuntime, blockProcessor); + + asciidoctorModule.block_processor( + getClassName(blockProcessor), + RubyUtils.toSymbol(rubyRuntime, blockName), groupName); + } + }); + return this; + } + + @Override + public ExtensionGroup block(final String blockName, final Class blockProcessor) { + registrations.add(new Runnable() { + @Override + public void run() { + javaImport(rubyRuntime, blockProcessor); + + asciidoctorModule.block_processor( + RubyUtils.toRubyClass(rubyRuntime, blockProcessor), + RubyUtils.toSymbol(rubyRuntime, blockName), groupName); + } + }); + return this; + } + + @Override + public ExtensionGroup block(final BlockProcessor blockProcessor) { + block(blockProcessor.getName(), blockProcessor); + return this; + } + + @Override + public ExtensionGroup block(final String blockName, final BlockProcessor blockProcessor) { + registrations.add(new Runnable() { + @Override + public void run() { + javaImport(rubyRuntime, blockProcessor.getClass()); + + asciidoctorModule.block_processor( + blockProcessor, + RubyUtils.toSymbol(rubyRuntime, blockName), groupName); + } + }); + return this; + } + + @Override + public ExtensionGroup blockMacro(final String blockName, final Class blockMacroProcessor) { + registrations.add(new Runnable() { + @Override + public void run() { + javaImport(rubyRuntime, blockMacroProcessor); + asciidoctorModule.block_macro( + blockMacroProcessor.getSimpleName(), + RubyUtils.toSymbol(rubyRuntime, blockName), groupName); + } + }); + return this; + } + + @Override + public ExtensionGroup blockMacro(final String blockName, final String blockMacroProcessor) { + registrations.add(new Runnable() { + @Override + public void run() { + javaImport(rubyRuntime, blockMacroProcessor); + asciidoctorModule.block_macro( + getClassName(blockMacroProcessor), + RubyUtils.toSymbol(rubyRuntime, blockName), groupName); + } + }); + return this; + } + + @Override + public ExtensionGroup blockMacro(final BlockMacroProcessor blockMacroProcessor) { + registrations.add(new Runnable() { + @Override + public void run() { + javaImport(rubyRuntime, blockMacroProcessor.getClass()); + asciidoctorModule.block_macro( + blockMacroProcessor, + RubyUtils.toSymbol(rubyRuntime, blockMacroProcessor.getName()), groupName); + } + }); + return this; + } + + @Override + public ExtensionGroup inlineMacro(final InlineMacroProcessor inlineMacroProcessor) { + registrations.add(new Runnable() { + @Override + public void run() { + javaImport(rubyRuntime, inlineMacroProcessor.getClass()); + + asciidoctorModule.inline_macro( + inlineMacroProcessor, + RubyUtils.toSymbol(rubyRuntime, inlineMacroProcessor.getName()), groupName); + } + }); + return this; + } + + @Override + public ExtensionGroup inlineMacro(final String blockName, final Class inlineMacroProcessor) { + registrations.add(new Runnable() { + @Override + public void run() { + javaImport(rubyRuntime, inlineMacroProcessor); + + asciidoctorModule.inline_macro( + RubyUtils.toRubyClass(rubyRuntime, inlineMacroProcessor), + RubyUtils.toSymbol(rubyRuntime, blockName), groupName); + } + }); + return this; + } + + @Override + public ExtensionGroup inlineMacro(final String blockName, final String inlineMacroProcessor) { + registrations.add(new Runnable() { + @Override + public void run() { + javaImport(rubyRuntime, inlineMacroProcessor); + + asciidoctorModule.inline_macro( + getClassName(inlineMacroProcessor), + RubyUtils.toSymbol(rubyRuntime, blockName), groupName); + } + }); + return this; + } + + @Override + public ExtensionGroup requireRubyLibrary(final String requiredLibrary) { + registrations.add(new Runnable() { + @Override + public void run() { + RubyUtils.requireLibrary(rubyRuntime, requiredLibrary); + } + }); + return this; + } + + @Override + public ExtensionGroup loadRubyClass(final InputStream rubyClassStream) { + registrations.add(new Runnable() { + @Override + public void run() { + RubyUtils.loadRubyClass(rubyRuntime, rubyClassStream); + } + }); + return this; + } + + @Override + public ExtensionGroup rubyPreprocessor(final String preprocessor) { + registrations.add(new Runnable() { + @Override + public void run() { + asciidoctorModule.preprocessor(preprocessor, groupName); + } + }); + return this; + } + + @Override + public ExtensionGroup rubyPostprocessor(final String postprocessor) { + registrations.add(new Runnable() { + @Override + public void run() { + asciidoctorModule.postprocessor(postprocessor, groupName); + } + }); + return this; + } + + @Override + public ExtensionGroup rubyDocinfoProcessor(final String docinfoProcessor) { + registrations.add(new Runnable() { + @Override + public void run() { + asciidoctorModule.docinfo_processor(docinfoProcessor, groupName); + } + }); + return this; + } + + @Override + public ExtensionGroup rubyIncludeProcessor(final String includeProcessor) { + registrations.add(new Runnable() { + @Override + public void run() { + asciidoctorModule.include_processor(includeProcessor, groupName); + } + }); + return this; + } + + @Override + public ExtensionGroup rubyTreeprocessor(final String treeProcessor) { + registrations.add(new Runnable() { + @Override + public void run() { + asciidoctorModule.treeprocessor(treeProcessor, groupName); + } + }); + return this; + } + + @Override + public ExtensionGroup rubyBlock(final String blockName, final String blockProcessor) { + registrations.add(new Runnable() { + @Override + public void run() { + asciidoctorModule.block_processor( + blockProcessor, RubyUtils.toSymbol(rubyRuntime, blockName), groupName); + } + }); + return this; + } + + @Override + public ExtensionGroup rubyBlockMacro(final String blockName, final String blockMacroProcessor) { + registrations.add(new Runnable() { + @Override + public void run() { + asciidoctorModule.block_macro( + blockMacroProcessor, RubyUtils.toSymbol(rubyRuntime, blockName), groupName); + } + }); + return this; + } + + @Override + public ExtensionGroup rubyInlineMacro(final String blockName, final String inlineMacroProcessor) { + registrations.add(new Runnable() { + @Override + public void run() { + asciidoctorModule.inline_macro( + inlineMacroProcessor, RubyUtils.toSymbol(rubyRuntime, blockName), groupName); + } + }); + return this; + } + + private void javaImport(Ruby ruby, Class clazz) { + ruby.evalScriptlet(String.format("java_import '%s'", getImportLine(clazz))); + } + + private void javaImport(Ruby ruby, String className) { + ruby.evalScriptlet(String.format("java_import '%s'", className)); + } + + private String getImportLine(Class extensionClass) { + int dollarPosition = -1; + String className = extensionClass.getName(); + if ((dollarPosition = className.indexOf("$")) != -1) { + className = className.substring(0, dollarPosition); + } + return className; + } + + private String getClassName(String clazz) { + return clazz.substring(clazz.lastIndexOf(".")+1); + } + +} diff --git a/asciidoctorj-core/src/main/java/org/asciidoctor/internal/JRubyAsciidoctor.java b/asciidoctorj-core/src/main/java/org/asciidoctor/internal/JRubyAsciidoctor.java index e6947c488..0d611d6ef 100644 --- a/asciidoctorj-core/src/main/java/org/asciidoctor/internal/JRubyAsciidoctor.java +++ b/asciidoctorj-core/src/main/java/org/asciidoctor/internal/JRubyAsciidoctor.java @@ -14,6 +14,7 @@ import org.asciidoctor.ast.Title; import org.asciidoctor.converter.JavaConverterRegistry; import org.asciidoctor.converter.internal.ConverterRegistryExecutor; +import org.asciidoctor.extension.ExtensionGroup; import org.asciidoctor.extension.JavaExtensionRegistry; import org.asciidoctor.extension.RubyExtensionRegistry; import org.asciidoctor.extension.internal.ExtensionRegistryExecutor; @@ -37,6 +38,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.UUID; import java.util.logging.Logger; public class JRubyAsciidoctor implements Asciidoctor { @@ -599,6 +601,23 @@ public Document load(String content, Map options) { public Document loadFile(File file, Map options) { RubyHash rubyHash = RubyHashUtil.convertMapToRubyHashWithSymbols(rubyRuntime, options); return new Document(this.asciidoctorModule.load_file(file.getAbsolutePath(), rubyHash), this.rubyRuntime); + } + + Ruby getRubyRuntime() { + return this.rubyRuntime; + } + + AsciidoctorModule getAsciidoctorModule() { + return asciidoctorModule; + } + @Override + public ExtensionGroup createGroup() { + return new ExtensionGroupImpl(UUID.randomUUID().toString(), this); + } + + @Override + public ExtensionGroup createGroup(String groupName) { + return new ExtensionGroupImpl(groupName, this); } } diff --git a/asciidoctorj-core/src/main/resources/org/asciidoctor/internal/asciidoctorclass.rb b/asciidoctorj-core/src/main/resources/org/asciidoctor/internal/asciidoctorclass.rb index da34f73b7..f05927e8f 100644 --- a/asciidoctorj-core/src/main/resources/org/asciidoctor/internal/asciidoctorclass.rb +++ b/asciidoctorj-core/src/main/resources/org/asciidoctor/internal/asciidoctorclass.rb @@ -4,7 +4,7 @@ module Extensions include_package 'org.asciidoctor.extension' # Treeprocessor was renamed in to TreeProcessor in https://github.com/asciidoctor/asciidoctor/commit/f1dd816ade9db457b899581841e4cf7b788aa26d # This is necessary to run against both Asciidoctor 1.5.5 and 1.5.6 - TreeProcessor = Treeprocessor + TreeProcessor = Treeprocessor unless defined? TreeProcessor end end @@ -19,50 +19,54 @@ def unregister_all_extensions() Asciidoctor::Extensions.unregister_all end - def docinfo_processor(extensionName) - Asciidoctor::Extensions.register do + def unregister_extension name + Asciidoctor::Extensions.unregister name + end + + def docinfo_processor(extensionName, groupName = nil) + Asciidoctor::Extensions.register groupName do docinfo_processor extensionName end end - def treeprocessor(extensionName) - Asciidoctor::Extensions.register do + def treeprocessor(extensionName, groupName = nil) + Asciidoctor::Extensions.register groupName do treeprocessor extensionName end end - def include_processor(extensionName) - Asciidoctor::Extensions.register do + def include_processor(extensionName, groupName = nil) + Asciidoctor::Extensions.register groupName do include_processor extensionName end end - def preprocessor(extensionName) - Asciidoctor::Extensions.register do + def preprocessor(extensionName, groupName = nil) + Asciidoctor::Extensions.register groupName do preprocessor extensionName end end - def postprocessor(extensionName) - Asciidoctor::Extensions.register do + def postprocessor(extensionName, groupName = nil) + Asciidoctor::Extensions.register groupName do postprocessor extensionName end end - def block_processor(extensionName, blockSymbol) - Asciidoctor::Extensions.register do + def block_processor(extensionName, blockSymbol, groupName = nil) + Asciidoctor::Extensions.register groupName do block extensionName, blockSymbol end end - def block_macro(extensionName, blockSymbol) - Asciidoctor::Extensions.register do + def block_macro(extensionName, blockSymbol, groupName = nil) + Asciidoctor::Extensions.register groupName do block_macro extensionName, blockSymbol end end - def inline_macro(extensionName, blockSymbol) - Asciidoctor::Extensions.register do + def inline_macro(extensionName, blockSymbol, groupName = nil) + Asciidoctor::Extensions.register groupName do inline_macro extensionName, blockSymbol end end diff --git a/asciidoctorj-core/src/test/java/org/asciidoctor/extension/WhenJavaExtensionIsRegistered.java b/asciidoctorj-core/src/test/java/org/asciidoctor/extension/WhenJavaExtensionIsRegistered.java index 06acc4156..cd3b7ca08 100644 --- a/asciidoctorj-core/src/test/java/org/asciidoctor/extension/WhenJavaExtensionIsRegistered.java +++ b/asciidoctorj-core/src/test/java/org/asciidoctor/extension/WhenJavaExtensionIsRegistered.java @@ -1,26 +1,5 @@ package org.asciidoctor.extension; -import static org.asciidoctor.OptionsBuilder.options; -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.not; -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.hamcrest.CoreMatchers.startsWith; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; - -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - import org.asciidoctor.Asciidoctor; import org.asciidoctor.AttributesBuilder; import org.asciidoctor.Options; @@ -37,6 +16,28 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import static org.asciidoctor.OptionsBuilder.options; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.startsWith; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + public class WhenJavaExtensionIsRegistered { @Rule @@ -700,4 +701,99 @@ public void a_block_processor_instance_should_be_executed_when_registered_block_ } + @Test + public void should_unregister_postprocessor() throws IOException { + + // Given: A registered Postprocessor + ExtensionGroup extensionGroup = asciidoctor.createGroup(UUID.randomUUID().toString()) + .postprocessor(CustomFooterPostProcessor.class); + + // When: I render a document without registering the ExtensionGroup + { + Options options = options().inPlace(false).toFile(new File(testFolder.getRoot(), "rendersample.html")) + .safe(SafeMode.UNSAFE).get(); + + asciidoctor.renderFile(classpath.getResource("rendersample.asciidoc"), options); + + // Then: it is invoked + File renderedFile = new File(testFolder.getRoot(), "rendersample.html"); + org.jsoup.nodes.Document doc = Jsoup.parse(renderedFile, "UTF-8"); + Element footer = doc.getElementById("footer-text"); + assertThat(footer.text(), not(containsString("Copyright Acme, Inc."))); + } + + // When: I register the ExtensionGroup and render a document + { + extensionGroup.register(); + Options options = options().inPlace(false).toFile(new File(testFolder.getRoot(), "rendersample.html")) + .safe(SafeMode.UNSAFE).get(); + + asciidoctor.renderFile(classpath.getResource("rendersample.asciidoc"), options); + + // Then: it is invoked + File renderedFile = new File(testFolder.getRoot(), "rendersample.html"); + org.jsoup.nodes.Document doc = Jsoup.parse(renderedFile, "UTF-8"); + Element footer = doc.getElementById("footer-text"); + assertThat(footer.text(), containsString("Copyright Acme, Inc.")); + } + // When: I unregister the Postprocessor and render again with the same Asciidoctor instance + { + extensionGroup.unregister();; + + Options options2 = options().inPlace(false).toFile(new File(testFolder.getRoot(), "rendersample2.html")) + .safe(SafeMode.UNSAFE).get(); + asciidoctor.renderFile(classpath.getResource("rendersample.asciidoc"), options2); + File renderedFile2 = new File(testFolder.getRoot(), "rendersample2.html"); + org.jsoup.nodes.Document doc2 = Jsoup.parse(renderedFile2, "UTF-8"); + + Element footer2 = doc2.getElementById("footer-text"); + assertThat(footer2.text(), not(containsString("Copyright Acme, Inc."))); + } + } + + @Test + public void should_unregister_block_processor() + throws IOException { + + Map config = new HashMap(); + config.put("contexts", Arrays.asList(":paragraph")); + config.put("content_model", ":simple"); + YellBlock yellBlock = new YellBlock("yell", config); + + ExtensionGroup extensionGroup = this.asciidoctor.createGroup().block(yellBlock); + + { + String contentWithoutBlock = asciidoctor.renderFile( + classpath.getResource("sample-with-yell-block.ad"), + options().toFile(false).get()); + Document docWithoutBlock = Jsoup.parse(contentWithoutBlock, "UTF-8"); + Elements elementsWithoutBlock = docWithoutBlock.getElementsByClass("paragraph"); + assertThat(elementsWithoutBlock.size(), is(1)); + assertThat(elementsWithoutBlock.get(0).text(), not(is("THE TIME IS NOW. GET A MOVE ON."))); + } + + { + extensionGroup.register(); + String content = asciidoctor.renderFile( + classpath.getResource("sample-with-yell-block.ad"), + options().toFile(false).get()); + Document doc = Jsoup.parse(content, "UTF-8"); + Elements elements = doc.getElementsByClass("paragraph"); + assertThat(elements.size(), is(1)); + assertThat(elements.get(0).text(), is("THE TIME IS NOW. GET A MOVE ON.")); + } + + { + extensionGroup.unregister(); + String contentWithoutBlock = asciidoctor.renderFile( + classpath.getResource("sample-with-yell-block.ad"), + options().toFile(false).get()); + Document docWithoutBlock = Jsoup.parse(contentWithoutBlock, "UTF-8"); + Elements elementsWithoutBlock = docWithoutBlock.getElementsByClass("paragraph"); + assertThat(elementsWithoutBlock.size(), is(1)); + assertThat(elementsWithoutBlock.get(0).text(), not(is("THE TIME IS NOW. GET A MOVE ON."))); + } + } + + } diff --git a/asciidoctorj-core/src/test/java/org/asciidoctor/extension/WhenRubyExtensionIsRegistered.java b/asciidoctorj-core/src/test/java/org/asciidoctor/extension/WhenRubyExtensionIsRegistered.java index 865dd89b6..b833a16e1 100644 --- a/asciidoctorj-core/src/test/java/org/asciidoctor/extension/WhenRubyExtensionIsRegistered.java +++ b/asciidoctorj-core/src/test/java/org/asciidoctor/extension/WhenRubyExtensionIsRegistered.java @@ -1,9 +1,5 @@ package org.asciidoctor.extension; -import static org.asciidoctor.OptionsBuilder.options; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; - import org.asciidoctor.Asciidoctor; import org.asciidoctor.internal.JRubyAsciidoctor; import org.asciidoctor.util.ClasspathResources; @@ -13,6 +9,11 @@ import org.junit.Rule; import org.junit.Test; +import static org.asciidoctor.OptionsBuilder.options; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertThat; + public class WhenRubyExtensionIsRegistered { @Rule @@ -37,4 +38,46 @@ public void ruby_extension_should_be_registered() { } + @Test + public void ruby_extension_should_be_unregistered() { + + ExtensionGroup extensionGroup = this.asciidoctor.createGroup() + .loadRubyClass(Class.class.getResourceAsStream("/YellRubyBlock.rb")) + .rubyBlock("rubyyell", "YellRubyBlock"); + + { + String contentWithoutBlock = asciidoctor.renderFile( + classpath.getResource("sample-with-ruby-yell-block.ad"), + options().toFile(false).get()); + + Document docWithoutBlock = Jsoup.parse(contentWithoutBlock, "UTF-8"); + Elements elementsWithoutBlock = docWithoutBlock.getElementsByClass("paragraph"); + assertThat(elementsWithoutBlock.size(), is(2)); + assertThat(elementsWithoutBlock.get(1).text(), not(is("THE TIME IS NOW! GET A MOVE ON!"))); + } + { + extensionGroup.register(); + String content = asciidoctor.renderFile( + classpath.getResource("sample-with-ruby-yell-block.ad"), + options().toFile(false).get()); + + Document doc = Jsoup.parse(content, "UTF-8"); + Elements elements = doc.getElementsByClass("paragraph"); + assertThat(elements.size(), is(2)); + assertThat(elements.get(1).text(), is("THE TIME IS NOW! GET A MOVE ON!")); + } + { + extensionGroup.unregister(); + + String contentWithoutBlock = asciidoctor.renderFile( + classpath.getResource("sample-with-ruby-yell-block.ad"), + options().toFile(false).get()); + + Document docWithoutBlock = Jsoup.parse(contentWithoutBlock, "UTF-8"); + Elements elementsWithoutBlock = docWithoutBlock.getElementsByClass("paragraph"); + assertThat(elementsWithoutBlock.size(), is(2)); + assertThat(elementsWithoutBlock.get(1).text(), not(is("THE TIME IS NOW! GET A MOVE ON!"))); + } + } + }