From 50862485a16d36b0d77558968798b79f6e186ffb Mon Sep 17 00:00:00 2001 From: Abel Salgado Romero Date: Sat, 8 Apr 2023 18:25:54 +0200 Subject: [PATCH] Add getImages() to Catalog * Current implementation works correctly when using load+getContent, convert and convertFile. * Fix 'convert' & 'convertFile' to correctly return Document type when applies * Fix WhenTwoAsciidoctorInstancesAreCreated test, not being run and having invalid code * Format RubyGemsPreloader.java and use immutable Map for options * Remove unused method JRubyAsciidoctor.scanForAsciiDocFiles --- CHANGELOG.adoc | 2 + .../java/org/asciidoctor/ast/Catalog.java | 9 + .../org/asciidoctor/ast/ImageReference.java | 8 + .../jruby/ast/impl/CatalogImpl.java | 27 ++- .../jruby/ast/impl/FootnoteImpl.java | 21 +- .../jruby/ast/impl/ImageReferenceImpl.java | 39 ++++ .../jruby/internal/JRubyAsciidoctor.java | 32 ++- .../jruby/internal/RubyGemsPreloader.java | 35 ++-- ...enTwoAsciidoctorInstancesAreCreated.groovy | 12 +- .../converter/WhenFootnotesAreUsed.groovy | 34 ++-- .../WhenReaderIsManipulatedInExtension.java | 12 +- .../jruby/ast/impl/TestImageReference.java | 33 +++ .../WhenReadingImagesFromCatalogAsset.java | 188 ++++++++++++++++++ ...ngImagesFromCatalogAssetFromConverter.java | 145 ++++++++++++++ .../test/resources/sample-with-images.adoc | 9 + 15 files changed, 523 insertions(+), 83 deletions(-) create mode 100644 asciidoctorj-api/src/main/java/org/asciidoctor/ast/ImageReference.java create mode 100644 asciidoctorj-core/src/main/java/org/asciidoctor/jruby/ast/impl/ImageReferenceImpl.java rename asciidoctorj-core/src/test/{java => groovy}/org/asciidoctor/WhenTwoAsciidoctorInstancesAreCreated.groovy (70%) create mode 100644 asciidoctorj-core/src/test/java/org/asciidoctor/jruby/ast/impl/TestImageReference.java create mode 100644 asciidoctorj-core/src/test/java/org/asciidoctor/jruby/internal/WhenReadingImagesFromCatalogAsset.java create mode 100644 asciidoctorj-core/src/test/java/org/asciidoctor/jruby/internal/WhenReadingImagesFromCatalogAssetFromConverter.java create mode 100644 asciidoctorj-core/src/test/resources/sample-with-images.adoc diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 31ebaf20b..53dee32bb 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -33,6 +33,8 @@ Improvement:: * Add required `--add-opens` to cli launch script to remove Jdk warnings (#1155) (@abelsromero) * Rename deprecated `headerFooter` option to the new `standalone` with same functionality (#1155) (@abelsromero) * Remove class `AsciidoctorUtils` to remove complexity and unused logging (#1169) (@abelsromero) +* Expose ImageReferences in the catalog (#1166) (@abelsromero) +* Return Document AST when using convert or convertFile with appropriate options (#1171) (@abelsromero) Bug Fixes:: diff --git a/asciidoctorj-api/src/main/java/org/asciidoctor/ast/Catalog.java b/asciidoctorj-api/src/main/java/org/asciidoctor/ast/Catalog.java index 41b0ea965..4cacd650c 100644 --- a/asciidoctorj-api/src/main/java/org/asciidoctor/ast/Catalog.java +++ b/asciidoctorj-api/src/main/java/org/asciidoctor/ast/Catalog.java @@ -15,6 +15,14 @@ public interface Catalog { */ List getFootnotes(); + /** + * Retrieves the images from the source document. + * Note that inline images are only available after `Document.getContent()` has been called. + * + * @return images occurring in document. + */ + List getImages(); + /** * Refs is a map of asciidoctor ids to asciidoctor document elements. * @@ -29,4 +37,5 @@ public interface Catalog { * @return a map of ids to elements that asciidoctor has collected from the document. */ Map getRefs(); + } diff --git a/asciidoctorj-api/src/main/java/org/asciidoctor/ast/ImageReference.java b/asciidoctorj-api/src/main/java/org/asciidoctor/ast/ImageReference.java new file mode 100644 index 000000000..c8688a727 --- /dev/null +++ b/asciidoctorj-api/src/main/java/org/asciidoctor/ast/ImageReference.java @@ -0,0 +1,8 @@ +package org.asciidoctor.ast; + +public interface ImageReference { + + String getTarget(); + + String getImagesdir(); +} diff --git a/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/ast/impl/CatalogImpl.java b/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/ast/impl/CatalogImpl.java index b44672dc4..ce0f90adb 100644 --- a/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/ast/impl/CatalogImpl.java +++ b/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/ast/impl/CatalogImpl.java @@ -2,17 +2,18 @@ import org.asciidoctor.ast.Catalog; import org.asciidoctor.ast.Footnote; +import org.asciidoctor.ast.ImageReference; import org.asciidoctor.jruby.internal.RubyHashMapDecorator; import org.jruby.RubyArray; import org.jruby.RubyHash; import org.jruby.RubyStruct; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; -public class CatalogImpl implements Catalog { +public class CatalogImpl implements Catalog { private final Map catalog; @@ -22,17 +23,23 @@ public CatalogImpl(Map catalog) { @Override public List getFootnotes() { - RubyArray rubyFootnotes = (RubyArray) catalog.get("footnotes"); - List footnotes = new ArrayList<>(); - for (Object f : rubyFootnotes) { - footnotes.add(FootnoteImpl.getInstance((RubyStruct) f)); - } - return footnotes; + return (List) ((RubyArray) catalog.get("footnotes")) + .stream() + .map(o -> FootnoteImpl.getInstance((RubyStruct) o)) + .collect(Collectors.toUnmodifiableList()); + } + + @Override + public List getImages() { + return (List) ((RubyArray) catalog.get("images")) + .stream() + .map(o -> ImageReferenceImpl.getInstance((RubyStruct) o)) + .collect(Collectors.toUnmodifiableList()); } @Override public Map getRefs() { - Map refs = new RubyHashMapDecorator((RubyHash) catalog.get("refs"), String.class); + Map refs = new RubyHashMapDecorator((RubyHash) catalog.get("refs"), String.class); return Collections.unmodifiableMap(refs); } -} \ No newline at end of file +} diff --git a/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/ast/impl/FootnoteImpl.java b/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/ast/impl/FootnoteImpl.java index 2f337ee42..fbd824c2c 100644 --- a/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/ast/impl/FootnoteImpl.java +++ b/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/ast/impl/FootnoteImpl.java @@ -1,8 +1,7 @@ package org.asciidoctor.jruby.ast.impl; -import org.jruby.RubyStruct; - import org.asciidoctor.ast.Footnote; +import org.jruby.RubyStruct; import org.jruby.javasupport.JavaEmbedUtils; public class FootnoteImpl implements Footnote { @@ -15,11 +14,11 @@ public class FootnoteImpl implements Footnote { private String id; private String text; - private static Object aref(RubyStruct s, String key) { - return JavaEmbedUtils.rubyToJava(s.aref(s.getRuntime().newString(key))); + private static Object aref(RubyStruct s, String key) { + return JavaEmbedUtils.rubyToJava(s.aref(s.getRuntime().newString(key))); } - public static Footnote getInstance(Long index, String id, String text) { + public static Footnote getInstance(Long index, String id, String text) { FootnoteImpl footnote = new FootnoteImpl(); footnote.index = index; footnote.id = id; @@ -35,11 +34,17 @@ public static Footnote getInstance(RubyStruct rubyFootnote) { } @Override - public Long getIndex() { return index; } + public Long getIndex() { + return index; + } @Override - public String getId() { return id; } + public String getId() { + return id; + } @Override - public String getText() { return text; } + public String getText() { + return text; + } } diff --git a/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/ast/impl/ImageReferenceImpl.java b/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/ast/impl/ImageReferenceImpl.java new file mode 100644 index 000000000..146934b02 --- /dev/null +++ b/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/ast/impl/ImageReferenceImpl.java @@ -0,0 +1,39 @@ +package org.asciidoctor.jruby.ast.impl; + +import org.asciidoctor.ast.ImageReference; +import org.jruby.RubyStruct; +import org.jruby.javasupport.JavaEmbedUtils; + +public class ImageReferenceImpl implements ImageReference { + + private static final String IMAGESDIR_KEY_NAME = "imagesdir"; + private static final String TARGET_KEY_NAME = "target"; + + private final String target; + private final String imagesdir; + + private ImageReferenceImpl(String target, String imagesdir) { + this.target = target; + this.imagesdir = imagesdir; + } + + static ImageReference getInstance(RubyStruct rubyFootnote) { + final String target = (String) aref(rubyFootnote, TARGET_KEY_NAME); + final String imagesdir = (String) aref(rubyFootnote, IMAGESDIR_KEY_NAME); + return new ImageReferenceImpl(target, imagesdir); + } + + private static Object aref(RubyStruct s, String key) { + return JavaEmbedUtils.rubyToJava(s.aref(s.getRuntime().newString(key))); + } + + @Override + public String getTarget() { + return target; + } + + @Override + public String getImagesdir() { + return imagesdir; + } +} diff --git a/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/internal/JRubyAsciidoctor.java b/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/internal/JRubyAsciidoctor.java index 80a8f99a0..ef5945d19 100644 --- a/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/internal/JRubyAsciidoctor.java +++ b/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/internal/JRubyAsciidoctor.java @@ -10,7 +10,6 @@ import org.asciidoctor.extension.JavaExtensionRegistry; import org.asciidoctor.extension.RubyExtensionRegistry; import org.asciidoctor.jruby.AsciidoctorJRuby; -import org.asciidoctor.jruby.DirectoryWalker; import org.asciidoctor.jruby.ast.impl.DocumentHeaderImpl; import org.asciidoctor.jruby.ast.impl.NodeConverter; import org.asciidoctor.jruby.converter.internal.ConverterRegistryExecutor; @@ -216,10 +215,6 @@ private List convertAllFiles(Map options, final Iterable return asciidoctorContent; } - private List scanForAsciiDocFiles(DirectoryWalker directoryWalker) { - return directoryWalker.scan(); - } - @Override public void requireLibrary(String... library) { requireLibraries(Arrays.asList(library)); @@ -307,11 +302,7 @@ public T convert(String content, Map options, Class expec IRubyObject object = getAsciidoctorModule().callMethod("convert", Optional.ofNullable(content).map(rubyRuntime::newString).orElse(null), rubyHash); - if (NodeConverter.NodeType.DOCUMENT_CLASS.isInstance(object)) { - // If a document is rendered to a file Asciidoctor returns the document, we return null - return null; - } - return RubyUtils.rubyToJava(rubyRuntime, object, expectedResult); + return adaptReturn(object, expectedResult); } catch (RaiseException e) { logger.severe(e.getException().getClass().getCanonicalName()); throw new AsciidoctorCoreException(e); @@ -392,11 +383,7 @@ public T convertFile(File file, Map options, Class expect try { IRubyObject object = getAsciidoctorModule().callMethod("convert_file", rubyRuntime.newString(file.getAbsolutePath()), rubyHash); - if (NodeConverter.NodeType.DOCUMENT_CLASS.isInstance(object)) { - // If a document is rendered to a file Asciidoctor returns the document, we return null - return null; - } - return RubyUtils.rubyToJava(rubyRuntime, object, expectedResult); + return adaptReturn(object, expectedResult); } catch (RaiseException e) { logger.severe(e.getMessage()); @@ -407,6 +394,17 @@ public T convertFile(File file, Map options, Class expect } } + private T adaptReturn(IRubyObject object, Class expectedResult) { + if (NodeConverter.NodeType.DOCUMENT_CLASS.isInstance(object)) { + if (Document.class.isAssignableFrom(expectedResult)) { + return (T) NodeConverter.createASTNode(object); + } else { + return null; + } + } + return RubyUtils.rubyToJava(rubyRuntime, object, expectedResult); + } + @Override public String convertFile(File file, Options options) { return convertFile(file, options, String.class); @@ -472,7 +470,7 @@ public Document load(String content, Options options) { return (Document) NodeConverter.createASTNode(getAsciidoctorModule().callMethod("load", Optional.ofNullable(content).map(rubyRuntime::newString).orElse(null), rubyHash)); } - + @Override public Document loadFile(File file, Map options) { RubyHash rubyHash = RubyHashUtil.convertMapToRubyHashWithSymbols(rubyRuntime, options); @@ -488,7 +486,7 @@ public Document loadFile(File file, Options options) { return (Document) NodeConverter.createASTNode(getAsciidoctorModule().callMethod("load_file", rubyRuntime.newString(file.getAbsolutePath()), rubyHash)); } - + @Override public ExtensionGroup createGroup() { return createGroup(UUID.randomUUID().toString()); diff --git a/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/internal/RubyGemsPreloader.java b/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/internal/RubyGemsPreloader.java index 59aa78cf3..e57dfbb85 100644 --- a/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/internal/RubyGemsPreloader.java +++ b/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/internal/RubyGemsPreloader.java @@ -1,12 +1,11 @@ package org.asciidoctor.jruby.internal; -import java.util.HashMap; -import java.util.Map; - import org.asciidoctor.Attributes; import org.asciidoctor.Options; import org.jruby.Ruby; +import java.util.Map; + public class RubyGemsPreloader { private static final String CODERAY = "coderay"; @@ -15,18 +14,16 @@ public class RubyGemsPreloader { private static final String PDF = "pdf"; private static final String REVEALJS = "asciidoctor-revealjs"; - private static final Map optionToRequiredGem = new HashMap() { - { - put(Attributes.SOURCE_HIGHLIGHTER, "require 'coderay'"); - put(Options.ERUBY, "require 'erubis'"); - put(Options.TEMPLATE_DIRS, "require 'tilt'"); - put(Attributes.DATA_URI, "require 'base64'"); - put(Attributes.CACHE_URI, "require 'open-uri/cached'"); - put(EPUB3, "require 'asciidoctor-epub3'"); - put(PDF, "require 'asciidoctor-pdf'"); - put(REVEALJS, "require 'asciidoctor-revealjs'"); - } - }; + private static final Map optionToRequiredGem = Map.of( + Options.ERUBY, "require 'erubis'", + Options.TEMPLATE_DIRS, "require 'tilt'", + Attributes.CACHE_URI, "require 'open-uri/cached'", + Attributes.DATA_URI, "require 'base64'", + Attributes.SOURCE_HIGHLIGHTER, "require 'coderay'", + EPUB3, "require 'asciidoctor-epub3'", + PDF, "require 'asciidoctor-pdf'", + REVEALJS, "require 'asciidoctor-revealjs'" + ); private Ruby rubyRuntime; @@ -60,16 +57,16 @@ && isOptionWithValue(attributes, Attributes.SOURCE_HIGHLIGHTER, CODERAY)) { if (isOptionSet(options, Options.TEMPLATE_DIRS)) { preloadLibrary(Options.TEMPLATE_DIRS); } - - if(isOptionSet(options, Options.BACKEND) && "epub3".equalsIgnoreCase((String) options.get(Options.BACKEND))) { + + if (isOptionSet(options, Options.BACKEND) && "epub3".equalsIgnoreCase((String) options.get(Options.BACKEND))) { preloadLibrary(EPUB3); } - if(isOptionSet(options, Options.BACKEND) && "pdf".equalsIgnoreCase((String) options.get(Options.BACKEND))) { + if (isOptionSet(options, Options.BACKEND) && "pdf".equalsIgnoreCase((String) options.get(Options.BACKEND))) { preloadLibrary(PDF); } - if(isOptionSet(options, Options.BACKEND) && "revealjs".equalsIgnoreCase((String) options.get(Options.BACKEND))) { + if (isOptionSet(options, Options.BACKEND) && "revealjs".equalsIgnoreCase((String) options.get(Options.BACKEND))) { preloadLibrary(REVEALJS); } } diff --git a/asciidoctorj-core/src/test/java/org/asciidoctor/WhenTwoAsciidoctorInstancesAreCreated.groovy b/asciidoctorj-core/src/test/groovy/org/asciidoctor/WhenTwoAsciidoctorInstancesAreCreated.groovy similarity index 70% rename from asciidoctorj-core/src/test/java/org/asciidoctor/WhenTwoAsciidoctorInstancesAreCreated.groovy rename to asciidoctorj-core/src/test/groovy/org/asciidoctor/WhenTwoAsciidoctorInstancesAreCreated.groovy index 29551234c..a9469f31b 100644 --- a/asciidoctorj-core/src/test/java/org/asciidoctor/WhenTwoAsciidoctorInstancesAreCreated.groovy +++ b/asciidoctorj-core/src/test/groovy/org/asciidoctor/WhenTwoAsciidoctorInstancesAreCreated.groovy @@ -1,7 +1,6 @@ package org.asciidoctor import org.asciidoctor.ast.Block -import org.asciidoctor.ast.ContentNode import org.asciidoctor.ast.StructuralNode import org.asciidoctor.extension.BlockMacroProcessor import spock.lang.Specification @@ -13,7 +12,6 @@ class WhenTwoAsciidoctorInstancesAreCreated extends Specification { private static final String TEST_STRING = 'Hello World' def "then every Asciidoctor instance has its own extension registry"() { - given: String document = '''= Test document @@ -21,17 +19,17 @@ testmacro::Test[] ''' when: - Asciidoctor asciidoctor1 = Asciidoctor.Factory.create(getClass().classLoader) - Asciidoctor asciidoctor2 = Asciidoctor.Factory.create(getClass().classLoader) + Asciidoctor asciidoctor1 = Asciidoctor.Factory.create() + Asciidoctor asciidoctor2 = Asciidoctor.Factory.create() asciidoctor1.javaExtensionRegistry().blockMacro('testmacro', TestBlockMacroProcessor) then: - asciidoctor1.convert(document, OptionsBuilder.options().standalone(false)).contains(TEST_STRING) - !asciidoctor2.convert(document, OptionsBuilder.options().standalone(false)).contains(TEST_STRING) + def standaloneDisabledOptions = Options.builder().standalone(false).build() + asciidoctor1.convert(document, standaloneDisabledOptions).contains(TEST_STRING) + !asciidoctor2.convert(document, standaloneDisabledOptions).contains(TEST_STRING) } - static class TestBlockMacroProcessor extends BlockMacroProcessor { TestBlockMacroProcessor(String macroName) { super(macroName) diff --git a/asciidoctorj-core/src/test/groovy/org/asciidoctor/converter/WhenFootnotesAreUsed.groovy b/asciidoctorj-core/src/test/groovy/org/asciidoctor/converter/WhenFootnotesAreUsed.groovy index 749639fad..939981cb0 100644 --- a/asciidoctorj-core/src/test/groovy/org/asciidoctor/converter/WhenFootnotesAreUsed.groovy +++ b/asciidoctorj-core/src/test/groovy/org/asciidoctor/converter/WhenFootnotesAreUsed.groovy @@ -1,7 +1,7 @@ package org.asciidoctor.converter import org.asciidoctor.Asciidoctor -import org.asciidoctor.OptionsBuilder +import org.asciidoctor.Options import org.asciidoctor.ast.ContentNode import org.asciidoctor.ast.Document import org.asciidoctor.ast.Footnote @@ -13,22 +13,23 @@ import org.junit.runner.RunWith import spock.lang.Specification import static org.hamcrest.Matchers.contains -import static org.hamcrest.Matchers.empty import static org.hamcrest.Matchers.samePropertyValuesAs import static org.junit.Assert.assertThat /** * Tests that footnotes can be accessed from converter. * - * Note that it is a current limitation of asciidoctor that footnotes are not available until - * after they have been converted for the document. + * Note that it is a current limitation of asciidoctor that footnotes are not + * available until after they have been converted for the document. */ @RunWith(ArquillianSputnik) class WhenFootnotesAreUsed extends Specification { + static final String CONVERTER_BACKEND = 'footnote' @ArquillianResource private Asciidoctor asciidoctor + private static List footnotesBeforeConvert private static List footnotesAfterConvert @@ -42,15 +43,15 @@ class WhenFootnotesAreUsed extends Specification { * we simply want to force the conversion to verify that footnotes * are populated. */ + @Override String convert(ContentNode node, String transform, Map opts) { if (node instanceof Document) { def doc = (Document) node - footnotesBeforeConvert = doc.catalog.footnotes.collect() + footnotesBeforeConvert = doc.catalog.footnotes doc.content - footnotesAfterConvert = doc.catalog.footnotes.collect() - } - else if (node instanceof StructuralNode) { + footnotesAfterConvert = doc.catalog.footnotes + } else if (node instanceof StructuralNode) { ((StructuralNode) node).content } } @@ -63,7 +64,8 @@ class WhenFootnotesAreUsed extends Specification { } def convert(String document) { - asciidoctor.convert(document, OptionsBuilder.options().backend(CONVERTER_BACKEND)) + def options = Options.builder().backend(CONVERTER_BACKEND).build() + asciidoctor.convert(document, options) } def footnote(Long index, String id, String text) { @@ -78,8 +80,8 @@ class WhenFootnotesAreUsed extends Specification { convert(document) then: - assertThat(footnotesBeforeConvert, empty()) - assertThat(footnotesAfterConvert, empty()) + footnotesBeforeConvert.isEmpty() + footnotesAfterConvert.isEmpty() } def 'when a footnote is is in source doc, it should be accessible from converter'() { @@ -90,9 +92,9 @@ class WhenFootnotesAreUsed extends Specification { convert(document) then: - assertThat(footnotesBeforeConvert, empty()) + footnotesBeforeConvert.isEmpty() assertThat(footnotesAfterConvert, - contains(samePropertyValuesAs(footnote(1,'fid','we shall find out!')))) + contains(samePropertyValuesAs(footnote(1, 'fid', 'we shall find out!')))) } def 'when footnotes are in source doc, they should be accessible from the converter'() { @@ -108,9 +110,9 @@ An existing footnote can be referenced.footnote:myid1[] convert(document) then: - assertThat(footnotesBeforeConvert, empty()) + footnotesBeforeConvert.isEmpty() assertThat(footnotesAfterConvert, - contains(samePropertyValuesAs(footnote(1,'myid1','first footnote')), - samePropertyValuesAs(footnote(2, null, 'second footnote')))) + contains(samePropertyValuesAs(footnote(1, 'myid1', 'first footnote')), + samePropertyValuesAs(footnote(2, null, 'second footnote')))) } } diff --git a/asciidoctorj-core/src/test/java/org/asciidoctor/extension/WhenReaderIsManipulatedInExtension.java b/asciidoctorj-core/src/test/java/org/asciidoctor/extension/WhenReaderIsManipulatedInExtension.java index b6a74199f..165a081be 100644 --- a/asciidoctorj-core/src/test/java/org/asciidoctor/extension/WhenReaderIsManipulatedInExtension.java +++ b/asciidoctorj-core/src/test/java/org/asciidoctor/extension/WhenReaderIsManipulatedInExtension.java @@ -9,7 +9,7 @@ import org.junit.runner.RunWith; import java.io.File; -import java.util.HashMap; +import java.util.Map; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; @@ -32,7 +32,7 @@ public void currentLineNumberShouldBeReturned() { javaExtensionRegistry.preprocessor(NumberLinesPreprocessor.class); File inputFile = classpath.getResource("rendersample.asciidoc"); - asciidoctor.convertFile(inputFile, new HashMap()); + asciidoctor.convertFile(inputFile, Map.of()); File outpuFile = new File(inputFile.getParent(), "rendersample.asciidoc"); assertThat(outpuFile.exists(), is(true)); @@ -48,10 +48,10 @@ public void hasMoreLinesShouldBeReturned() { asciidoctor.convertFile( classpath.getResource("rendersample.asciidoc"), - new HashMap()); + Map.of()); File inputFile = classpath.getResource("rendersample.asciidoc"); - asciidoctor.convertFile(inputFile, new HashMap()); + asciidoctor.convertFile(inputFile, Map.of()); File outpuFile = new File(inputFile.getParent(), "rendersample.asciidoc"); assertThat(outpuFile.exists(), is(true)); @@ -67,10 +67,10 @@ public void isNextLineEmptyShouldBeReturned() { asciidoctor.convertFile( classpath.getResource("rendersample.asciidoc"), - new HashMap()); + Map.of()); File inputFile = classpath.getResource("rendersample.asciidoc"); - asciidoctor.convertFile(inputFile, new HashMap()); + asciidoctor.convertFile(inputFile, Map.of()); File outpuFile = new File(inputFile.getParent(), "rendersample.asciidoc"); assertThat(outpuFile.exists(), is(true)); diff --git a/asciidoctorj-core/src/test/java/org/asciidoctor/jruby/ast/impl/TestImageReference.java b/asciidoctorj-core/src/test/java/org/asciidoctor/jruby/ast/impl/TestImageReference.java new file mode 100644 index 000000000..343eb6573 --- /dev/null +++ b/asciidoctorj-core/src/test/java/org/asciidoctor/jruby/ast/impl/TestImageReference.java @@ -0,0 +1,33 @@ +package org.asciidoctor.jruby.ast.impl; + +import org.asciidoctor.ast.ImageReference; + +/** + * Test implementation of {@link ImageReference}. + * Cannot use default implementation because it's package protected. + */ +public class TestImageReference implements ImageReference { + + private final String target; + private final String imagesdir; + + public TestImageReference(String target) { + this.target = target; + this.imagesdir = null; + } + + public TestImageReference(String target, String imagesdir) { + this.target = target; + this.imagesdir = imagesdir; + } + + @Override + public String getTarget() { + return target; + } + + @Override + public String getImagesdir() { + return imagesdir; + } +} diff --git a/asciidoctorj-core/src/test/java/org/asciidoctor/jruby/internal/WhenReadingImagesFromCatalogAsset.java b/asciidoctorj-core/src/test/java/org/asciidoctor/jruby/internal/WhenReadingImagesFromCatalogAsset.java new file mode 100644 index 000000000..85941f47a --- /dev/null +++ b/asciidoctorj-core/src/test/java/org/asciidoctor/jruby/internal/WhenReadingImagesFromCatalogAsset.java @@ -0,0 +1,188 @@ +package org.asciidoctor.jruby.internal; + +import org.asciidoctor.Asciidoctor; +import org.asciidoctor.Attributes; +import org.asciidoctor.Options; +import org.asciidoctor.SafeMode; +import org.asciidoctor.arquillian.api.Unshared; +import org.asciidoctor.ast.Document; +import org.asciidoctor.ast.ImageReference; +import org.asciidoctor.jruby.ast.impl.TestImageReference; +import org.asciidoctor.util.ClasspathResources; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.arquillian.test.api.ArquillianResource; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(Arquillian.class) +public class WhenReadingImagesFromCatalogAsset { + + @ArquillianResource + private ClasspathResources classpath; + + @ArquillianResource(Unshared.class) + private Asciidoctor asciidoctor; + + @ArquillianResource + private TemporaryFolder testFolder; + + static final TestImageReference[] BLOCK_IMAGES = new TestImageReference[]{ + new TestImageReference("images/block-image.jpg") + }; + + static final TestImageReference[] ALL_IMAGES = new TestImageReference[]{ + new TestImageReference("images/block-image.jpg"), + new TestImageReference("images/inline-image.png") + }; + + @Test + public void shouldReturnEmptyWhenThereAreNoImages() { + final Options options = Options.builder() + .catalogAssets(true) + .build(); + + Document document = asciidoctor.load("= Hello", options); + List images = document.getCatalog().getImages(); + + assertThat(images) + .isEmpty(); + } + + @Test + public void shouldReturnNullImagesDirWhenNotSet() { + final Options options = Options.builder() + .catalogAssets(true) + .build(); + final String content = getAsciiDocWithImagesContent(); + + Document document = asciidoctor.load(content, options); + List images = document.getCatalog().getImages(); + + assertThat(images) + .usingRecursiveFieldByFieldElementComparator() + .containsExactly(BLOCK_IMAGES); + } + + @Test + public void shouldReturnImagesDirWhenSet() { + final Options options = Options.builder() + .catalogAssets(true) + .attributes(Attributes.builder() + .imagesDir("some-path") + .build()) + .build(); + final String content = getAsciiDocWithImagesContent(); + + Document document = asciidoctor.load(content, options); + List images = document.getCatalog().getImages(); + + assertThat(images) + .usingRecursiveFieldByFieldElementComparator() + .containsExactly(new TestImageReference("images/block-image.jpg", "some-path")); + } + + @Test + public void shouldNotCatalogInlineImagesWhenNotConverting() { + final Options options = Options.builder() + .catalogAssets(true) + .build(); + final String content = getAsciiDocWithImagesContent(); + + Document document = asciidoctor.load(content, options); + + List images = document.getCatalog().getImages(); + assertThat(images) + .usingRecursiveFieldByFieldElementComparator() + .containsExactly(BLOCK_IMAGES); + } + + @Test + public void shouldCatalogInlineImagesWhenProcessingContentAfterLoad() { + final Options options = Options.builder() + .catalogAssets(true) + .build(); + final String content = getAsciiDocWithImagesContent(); + + Document document = asciidoctor.load(content, options); + document.getContent(); + + List images = document.getCatalog().getImages(); + assertThat(images) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder(ALL_IMAGES); + } + + @Test + public void shouldNotCatalogImagesWhenCatalogAssetsIsFalse() { + final Options options = Options.builder() + .catalogAssets(false) + .build(); + final String content = getAsciiDocWithImagesContent(); + + Document document = asciidoctor.load(content, options); + document.getContent(); + + List images = document.getCatalog().getImages(); + assertThat(images).isEmpty(); + } + + @Test + public void shouldCatalogAllImagesWhenUsingConvertFile() throws IOException { + final Options options = Options.builder() + .catalogAssets(true) + .safe(SafeMode.UNSAFE) + .toFile(testFolder.newFile()) + .build(); + final File file = getAsciiDocWithImagesFile(); + + var document = asciidoctor.convertFile(file, options, Document.class); + + assertThat(document) + .isInstanceOf(Document.class); + + List images = document.getCatalog().getImages(); + assertThat(images) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder(ALL_IMAGES); + } + + @Test + public void shouldCatalogAllImagesWhenUsingConvert() throws IOException { + final Options options = Options.builder() + .catalogAssets(true) + .safe(SafeMode.UNSAFE) + .toFile(testFolder.newFile()) + .build(); + final String content = getAsciiDocWithImagesContent(); + + var document = asciidoctor.convert(content, options, Document.class); + + assertThat(document) + .isInstanceOf(Document.class); + + List images = document.getCatalog().getImages(); + assertThat(images) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder(ALL_IMAGES); + } + + private String getAsciiDocWithImagesContent() { + try { + return Files.readString(getAsciiDocWithImagesFile().toPath()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private File getAsciiDocWithImagesFile() { + return classpath.getResource("sample-with-images.adoc"); + } +} diff --git a/asciidoctorj-core/src/test/java/org/asciidoctor/jruby/internal/WhenReadingImagesFromCatalogAssetFromConverter.java b/asciidoctorj-core/src/test/java/org/asciidoctor/jruby/internal/WhenReadingImagesFromCatalogAssetFromConverter.java new file mode 100644 index 000000000..ce49b3da5 --- /dev/null +++ b/asciidoctorj-core/src/test/java/org/asciidoctor/jruby/internal/WhenReadingImagesFromCatalogAssetFromConverter.java @@ -0,0 +1,145 @@ +package org.asciidoctor.jruby.internal; + +import org.asciidoctor.Asciidoctor; +import org.asciidoctor.Options; +import org.asciidoctor.arquillian.api.Unshared; +import org.asciidoctor.ast.ContentNode; +import org.asciidoctor.ast.Document; +import org.asciidoctor.ast.ImageReference; +import org.asciidoctor.ast.StructuralNode; +import org.asciidoctor.converter.StringConverter; +import org.asciidoctor.jruby.ast.impl.TestImageReference; +import org.asciidoctor.util.ClasspathResources; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.arquillian.test.api.ArquillianResource; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.IOException; +import java.nio.file.Files; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(Arquillian.class) +public class WhenReadingImagesFromCatalogAssetFromConverter { + + @ArquillianResource + private ClasspathResources classpath; + + @ArquillianResource(Unshared.class) + private Asciidoctor asciidoctor; + + static final String CONVERTER_BACKEND = "custom-backend"; + + static final TestImageReference[] BLOCK_IMAGES = new TestImageReference[]{ + new TestImageReference("images/block-image.jpg") + }; + + static final TestImageReference[] ALL_IMAGES = new TestImageReference[]{ + new TestImageReference("images/block-image.jpg"), + new TestImageReference("images/inline-image.png") + }; + + private static List imagesBeforeConvert; + private static List imagesAfterConvert; + + @Before + public void beforeEach() { + final var javaConverterRegistry = asciidoctor.javaConverterRegistry(); + javaConverterRegistry.converters().clear(); + javaConverterRegistry.register(TestConverter.class, CONVERTER_BACKEND); + + imagesBeforeConvert = null; + imagesAfterConvert = null; + } + + @Test + public void shouldReturnEmptyWhenThereAreNoImages() { + final String content = ""; + + convert(content); + + assertThat(imagesBeforeConvert).isEmpty(); + assertThat(imagesAfterConvert).isEmpty(); + } + + @Test + public void shouldNotCatalogAnyImageWhenUsingLoad() { + final String content = getAsciiDodWithImagesDocument(); + + load(content); + + assertThat(imagesBeforeConvert).isNull(); + assertThat(imagesAfterConvert).isNull(); + } + + @Test + public void shouldReturnAllImages() { + final String content = getAsciiDodWithImagesDocument(); + + convert(content); + + assertThat(imagesBeforeConvert) + .usingRecursiveFieldByFieldElementComparator() + .containsExactly(BLOCK_IMAGES); + assertThat(imagesAfterConvert) + .usingRecursiveFieldByFieldElementComparator() + .containsExactly(ALL_IMAGES); + } + + private String convert(String document) { + var options = optionsWithConverter(); + return asciidoctor.convert(document, options); + } + + private void load(String document) { + var options = optionsWithConverter(); + asciidoctor.load(document, options); + } + + private static Options optionsWithConverter() { + return Options.builder() + .catalogAssets(true) + .backend(CONVERTER_BACKEND) + .build(); + } + + private String getAsciiDodWithImagesDocument() { + try { + return Files.readString(classpath.getResource("sample-with-images.adoc").toPath()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static class TestConverter extends StringConverter { + + public TestConverter(String backend, Map opts) { + super(backend, opts); + } + + /* + * For this conversion test we do not care about the conversion result, + * we simply want to force the conversion to verify that images references + * are populated. + */ + + @Override + public String convert(ContentNode node, String transform, Map opts) { + String content = ""; + if (node instanceof Document) { + var doc = (Document) node; + imagesBeforeConvert = doc.getCatalog().getImages(); + // force content to process inline images + content = (String) doc.getContent(); + imagesAfterConvert = doc.getCatalog().getImages(); + } else if (node instanceof StructuralNode) { + content = (String) ((StructuralNode) node).getContent(); + } + return content; + } + } +} diff --git a/asciidoctorj-core/src/test/resources/sample-with-images.adoc b/asciidoctorj-core/src/test/resources/sample-with-images.adoc new file mode 100644 index 000000000..54c926c5c --- /dev/null +++ b/asciidoctorj-core/src/test/resources/sample-with-images.adoc @@ -0,0 +1,9 @@ += This is a title + +== A few images + +A block image + +image::images/block-image.jpg[] + +An inlined image image:images/inline-image.png[] in the text.