From 70695a3f9b61c4a69f93edf0f42b18744f634b13 Mon Sep 17 00:00:00 2001 From: Abel Salgado Romero Date: Tue, 21 Mar 2023 12:52:55 +0100 Subject: [PATCH] Accept null as empty content for load and convert Both empty and null String are accepted as valid for load/convert methods for safety. * Minor Javadoc fixed * Integrate WhenDocumentIsLoaded into WhenAsciiDocIsLoadedToDocument Fixes #1146 --- CHANGELOG.adoc | 1 + .../java/org/asciidoctor/Asciidoctor.java | 24 ++-- .../asciidoctor/jruby/internal/IOUtils.java | 6 +- .../jruby/internal/JRubyAsciidoctor.java | 4 +- .../WhenAnAsciidoctorClassIsInstantiated.java | 114 ++++++++++------- .../WhenAsciiDocIsLoadedToDocument.java | 120 +++++++++++++++++- .../jruby/ast/impl/WhenDocumentIsLoaded.java | 119 ----------------- 7 files changed, 202 insertions(+), 186 deletions(-) delete mode 100644 asciidoctorj-core/src/test/java/org/asciidoctor/jruby/ast/impl/WhenDocumentIsLoaded.java diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index f13ab2466..7fffae634 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -26,6 +26,7 @@ Improvement:: * Expose `sectnum` property in Section interface (#1121) * Replace use of deprecated 'numbered' attribute by 'sectnums' (#1123) (@abelsromero) * Expose `source` and `source_lines` use of deprecated 'numbered' in Document interface (#1145) (@abelsromero) +* Accept 'null' as valid input (same as empty string) for load and convert String methods (#) (@abelsromero) Bug Fixes:: diff --git a/asciidoctorj-api/src/main/java/org/asciidoctor/Asciidoctor.java b/asciidoctorj-api/src/main/java/org/asciidoctor/Asciidoctor.java index dd917d997..ceec98158 100644 --- a/asciidoctorj-api/src/main/java/org/asciidoctor/Asciidoctor.java +++ b/asciidoctorj-api/src/main/java/org/asciidoctor/Asciidoctor.java @@ -30,7 +30,7 @@ public interface Asciidoctor extends AutoCloseable { /** - * Parse the AsciiDoc source input into an Document {@link Document} and + * Parse the AsciiDoc source input into a Document {@link Document} and * convert it to the specified backend format. *

* Accepts input as String object. @@ -44,7 +44,7 @@ public interface Asciidoctor extends AutoCloseable { String convert(String content, Map options); /** - * Parse the AsciiDoc source input into an Document {@link Document} and + * Parse the AsciiDoc source input into a Document {@link Document} and * convert it to the specified backend format. *

* Accepts input as String object. @@ -60,7 +60,7 @@ public interface Asciidoctor extends AutoCloseable { T convert(String content, Map options, Class expectedResult); /** - * Parse the AsciiDoc source input into an Document {@link Document} and + * Parse the AsciiDoc source input into a Document {@link Document} and * convert it to the specified backend format. *

* Accepts input as String object. @@ -72,7 +72,7 @@ public interface Asciidoctor extends AutoCloseable { String convert(String content, Options options); /** - * Parse the AsciiDoc source input into an Document {@link Document} and + * Parse the AsciiDoc source input into a Document {@link Document} and * convert it to the specified backend format. *

* Accepts input as String object. @@ -86,7 +86,7 @@ public interface Asciidoctor extends AutoCloseable { T convert(String content, Options options, Class expectedResult); /** - * Parse the AsciiDoc source input into an Document {@link Document} and + * Parse the AsciiDoc source input into a Document {@link Document} and * convert it to the specified backend format. *

* Accepts input as String object. @@ -100,7 +100,7 @@ public interface Asciidoctor extends AutoCloseable { String convert(String content, OptionsBuilder options); /** - * Parse the AsciiDoc source input into an Document {@link Document} and + * Parse the AsciiDoc source input into a Document {@link Document} and * convert it to the specified backend format. *

* Accepts input as String object. @@ -162,7 +162,7 @@ void convert(Reader contentReader, Writer rendererWriter, OptionsBuilder options) throws IOException; /** - * Parse the AsciiDoc source input into an Document {@link Document} and + * Parse the AsciiDoc source input into a Document {@link Document} and * convert it to the specified backend format. *

* Accepts input as File. @@ -188,7 +188,7 @@ void convert(Reader contentReader, Writer rendererWriter, String convertFile(File file, Map options); /** - * Parse the AsciiDoc source input into an Document {@link Document} and + * Parse the AsciiDoc source input into a Document {@link Document} and * convert it to the specified backend format. *

* Accepts input as File. @@ -217,7 +217,7 @@ void convert(Reader contentReader, Writer rendererWriter, /** - * Parse the AsciiDoc source input into an Document {@link Document} and + * Parse the AsciiDoc source input into a Document {@link Document} and * convert it to the specified backend format. *

* Accepts input as File. @@ -241,7 +241,7 @@ void convert(Reader contentReader, Writer rendererWriter, String convertFile(File file, Options options); /** - * Parse the AsciiDoc source input into an Document {@link Document} and + * Parse the AsciiDoc source input into a Document {@link Document} and * convert it to the specified backend format. *

* Accepts input as File. @@ -267,7 +267,7 @@ void convert(Reader contentReader, Writer rendererWriter, T convertFile(File file, Options options, Class expectedResult); /** - * Parse the AsciiDoc source input into an Document {@link Document} and + * Parse the AsciiDoc source input into a Document {@link Document} and * convert it to the specified backend format. *

* Accepts input as File. @@ -293,7 +293,7 @@ void convert(Reader contentReader, Writer rendererWriter, String convertFile(File file, OptionsBuilder options); /** - * Parse the AsciiDoc source input into an Document {@link Document} and + * Parse the AsciiDoc source input into a Document {@link Document} and * convert it to the specified backend format. *

* Accepts input as File. diff --git a/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/internal/IOUtils.java b/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/internal/IOUtils.java index 5ac12a6ed..ca85394a0 100644 --- a/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/internal/IOUtils.java +++ b/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/internal/IOUtils.java @@ -4,6 +4,7 @@ import java.io.InputStream; import java.io.Reader; import java.io.Writer; +import java.util.Objects; import java.util.Scanner; public class IOUtils { @@ -13,13 +14,14 @@ private IOUtils() { } public static String readFull(InputStream inputStream) { - try (Scanner scanner = new Scanner(inputStream).useDelimiter("\\A")){ + try (Scanner scanner = new Scanner(inputStream).useDelimiter("\\A")) { return scanner.next(); } } public static String readFull(Reader reader) { - try (Scanner scanner = new Scanner(reader).useDelimiter("\\A")){ + Objects.requireNonNull(reader, "reader"); + try (Scanner scanner = new Scanner(reader).useDelimiter("\\A")) { return scanner.next(); } } 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 d7bf75ad2..542aaba15 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 @@ -308,7 +308,7 @@ public T convert(String content, Map options, Class expec try { IRubyObject object = getAsciidoctorModule().callMethod("convert", - rubyRuntime.newString(content), rubyHash); + 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; @@ -468,7 +468,7 @@ public Document load(String content, Map options) { public Document load(String content, Options options) { RubyHash rubyHash = RubyHashUtil.convertMapToRubyHashWithSymbols(rubyRuntime, options.map()); return (Document) NodeConverter.createASTNode(getAsciidoctorModule().callMethod("load", - rubyRuntime.newString(content), rubyHash)); + Optional.ofNullable(content).map(rubyRuntime::newString).orElse(null), rubyHash)); } @Override diff --git a/asciidoctorj-core/src/test/java/org/asciidoctor/WhenAnAsciidoctorClassIsInstantiated.java b/asciidoctorj-core/src/test/java/org/asciidoctor/WhenAnAsciidoctorClassIsInstantiated.java index b5d39d861..0b95a9936 100644 --- a/asciidoctorj-core/src/test/java/org/asciidoctor/WhenAnAsciidoctorClassIsInstantiated.java +++ b/asciidoctorj-core/src/test/java/org/asciidoctor/WhenAnAsciidoctorClassIsInstantiated.java @@ -1,38 +1,11 @@ package org.asciidoctor; -import static org.asciidoctor.AttributesBuilder.attributes; -import static org.asciidoctor.OptionsBuilder.options; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.nullValue; -import static org.hamcrest.collection.IsArrayWithSize.arrayWithSize; -import static org.junit.Assert.assertThat; -import static org.xmlmatchers.xpath.HasXPath.hasXPath; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.StringWriter; -import java.text.Format; -import java.text.SimpleDateFormat; -import java.util.Arrays; -import java.util.Calendar; -import java.util.HashMap; -import java.util.Map; - -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.Source; -import javax.xml.transform.dom.DOMSource; - +import com.google.common.io.CharStreams; import org.asciidoctor.arquillian.api.Unshared; import org.asciidoctor.jruby.AsciiDocDirectoryWalker; import org.asciidoctor.jruby.internal.AsciidoctorCoreException; import org.asciidoctor.util.ClasspathResources; +import org.assertj.core.api.Assertions; import org.jboss.arquillian.junit.Arquillian; import org.jboss.arquillian.test.api.ArquillianResource; import org.junit.Test; @@ -40,7 +13,26 @@ import org.junit.runner.RunWith; import org.xml.sax.SAXException; -import com.google.common.io.CharStreams; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Source; +import javax.xml.transform.dom.DOMSource; +import java.io.*; +import java.text.Format; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Calendar; +import java.util.HashMap; +import java.util.Map; + +import static org.asciidoctor.AttributesBuilder.attributes; +import static org.asciidoctor.OptionsBuilder.options; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.collection.IsArrayWithSize.arrayWithSize; +import static org.junit.Assert.assertThat; +import static org.xmlmatchers.xpath.HasXPath.hasXPath; @RunWith(Arquillian.class) public class WhenAnAsciidoctorClassIsInstantiated { @@ -54,6 +46,47 @@ public class WhenAnAsciidoctorClassIsInstantiated { @ArquillianResource(Unshared.class) private Asciidoctor asciidoctor; + @Test + public void should_accept_empty_string_as_empty_content_when_output_is_String() { + Options basicOptions = Options.builder().build(); + String result = asciidoctor.convert("", basicOptions); + + assertThat(result, isEmptyString()); + } + + @Test + public void should_accept_null_string_as_empty_content_when_output_is_String() { + Options basicOptions = Options.builder().build(); + String result = asciidoctor.convert(null, basicOptions); + + assertThat(result, isEmptyString()); + } + + @Test + public void should_accept_null_string_as_empty_content_when_output_is_File() { + File expectedFile = new File(testFolder.getRoot(), "expected_empty.html"); + Options options = Options.builder() + .safe(SafeMode.UNSAFE) + .toFile(expectedFile) + .build(); + String renderContent = asciidoctor.convert(null, options); + + assertThat(expectedFile.exists(), is(true)); + assertThat(renderContent, is(nullValue())); + } + + @Test + public void should_fail_when_reader_is_null() { + Options basicOptions = Options.builder().build(); + StringWriter writer = new StringWriter(); + + Throwable throwable = Assertions.catchThrowable(() -> asciidoctor.convert(null, writer, basicOptions)); + + Assertions.assertThat(throwable) + .isInstanceOf(NullPointerException.class) + .hasMessage("reader"); + } + @Test public void content_should_be_read_from_reader_and_written_to_writer() throws IOException, SAXException, ParserConfigurationException { @@ -78,8 +111,7 @@ public void file_document_should_be_rendered_into_default_backend() throws IOExc } @Test - public void file_document_should_be_rendered_into_current_directory_using_options_class() - throws FileNotFoundException, IOException, SAXException, ParserConfigurationException { + public void file_document_should_be_rendered_into_current_directory_using_options_class() { Options options = options().inPlace(true).get(); File inputFile = classpath.getResource("rendersample.asciidoc"); @@ -94,8 +126,7 @@ public void file_document_should_be_rendered_into_current_directory_using_option } @Test - public void file_document_should_be_rendered_into_current_directory() throws FileNotFoundException, IOException, - SAXException, ParserConfigurationException { + public void file_document_should_be_rendered_into_current_directory() { File inputFile = classpath.getResource("rendersample.asciidoc"); String renderContent = asciidoctor.convertFile(inputFile, options() @@ -110,8 +141,7 @@ public void file_document_should_be_rendered_into_current_directory() throws Fil } @Test - public void file_document_should_be_rendered_into_foreign_directory() throws FileNotFoundException, IOException, - SAXException, ParserConfigurationException { + public void file_document_should_be_rendered_into_foreign_directory() { Map options = options().inPlace(false).safe(SafeMode.UNSAFE).toDir(testFolder.getRoot()) .asMap(); @@ -140,8 +170,7 @@ public void file_document_should_be_rendered_from_base_dir() throws IOException } @Test - public void file_document_should_be_rendered_into_foreign_directory_using_options_class() - throws FileNotFoundException, IOException, SAXException, ParserConfigurationException { + public void file_document_should_be_rendered_into_foreign_directory_using_options_class() { Options options = options().inPlace(false).safe(SafeMode.UNSAFE).toDir(testFolder.getRoot()).get(); @@ -154,8 +183,7 @@ public void file_document_should_be_rendered_into_foreign_directory_using_option } @Test - public void docbook_document_should_be_rendered_into_current_directory() throws FileNotFoundException, IOException, - SAXException, ParserConfigurationException { + public void docbook_document_should_be_rendered_into_current_directory() { Map attributes = attributes().backend("docbook").asMap(); Map options = options().inPlace(true).attributes(attributes).asMap(); @@ -172,8 +200,7 @@ public void docbook_document_should_be_rendered_into_current_directory() throws } @Test - public void docbook_document_should_be_rendered_into_current_directory_using_options_class() - throws FileNotFoundException, IOException, SAXException, ParserConfigurationException { + public void docbook_document_should_be_rendered_into_current_directory_using_options_class() { Attributes attributes = attributes().backend("docbook").get(); Options options = options().inPlace(true).attributes(attributes).get(); @@ -190,8 +217,7 @@ public void docbook_document_should_be_rendered_into_current_directory_using_opt } @Test - public void docbook_document_should_be_rendered_into_current_directory_using_options_backend_attribute() - throws FileNotFoundException, IOException, SAXException, ParserConfigurationException { + public void docbook_document_should_be_rendered_into_current_directory_using_options_backend_attribute() { Options options = options().inPlace(true).backend("docbook").get(); diff --git a/asciidoctorj-core/src/test/java/org/asciidoctor/WhenAsciiDocIsLoadedToDocument.java b/asciidoctorj-core/src/test/java/org/asciidoctor/WhenAsciiDocIsLoadedToDocument.java index 4f965e885..d2b56e015 100644 --- a/asciidoctorj-core/src/test/java/org/asciidoctor/WhenAsciiDocIsLoadedToDocument.java +++ b/asciidoctorj-core/src/test/java/org/asciidoctor/WhenAsciiDocIsLoadedToDocument.java @@ -1,14 +1,10 @@ package org.asciidoctor; import org.asciidoctor.arquillian.api.Unshared; -import org.asciidoctor.ast.Author; -import org.asciidoctor.ast.Cell; -import org.asciidoctor.ast.Document; -import org.asciidoctor.ast.RevisionInfo; -import org.asciidoctor.ast.Section; -import org.asciidoctor.ast.StructuralNode; +import org.asciidoctor.ast.*; import org.asciidoctor.jruby.internal.IOUtils; import org.asciidoctor.util.ClasspathResources; +import org.assertj.core.api.Assertions; import org.jboss.arquillian.junit.Arquillian; import org.jboss.arquillian.test.api.ArquillianResource; import org.junit.Test; @@ -76,6 +72,23 @@ public class WhenAsciiDocIsLoadedToDocument { @ArquillianResource private ClasspathResources classpath = new ClasspathResources(); + @Test + public void should_return_empty_sources_when_document_is_null() { + assertEmptySources(asciidoctor.load(null, Options.builder().build())); + } + + @Test + public void should_return_empty_sources_when_document_is_empty() { + assertEmptySources(asciidoctor.load(null, Options.builder().build())); + } + + private static void assertEmptySources(Document document) { + String source = document.getSource(); + assertThat(source, isEmptyString()); + List sourceLines = document.getSourceLines(); + assertThat(sourceLines, hasSize(0)); + } + @Test public void should_return_section_blocks() { Document document = asciidoctor.load(DOCUMENT, new HashMap<>()); @@ -162,7 +175,7 @@ public void should_return_options_from_parsed_file_when_passed_as_options_object assertThat((Boolean) documentOptions.get("compact"), is(true)); } - + @Test public void should_return_node_name() { Document document = asciidoctor.load(DOCUMENT, new HashMap<>()); @@ -515,4 +528,97 @@ public void should_get_revision_info() { assertThat(revisionInfo.getNumber(), is("1.0")); assertThat(revisionInfo.getRemark(), is("First draft")); } + + @Test + public void should_return_source_and_source_lines() { + final String asciidoc = asciidocWithSections(); + + Document document = loadDocument(asciidoc); + + String source = document.getSource(); + Assertions.assertThat(source).isEqualTo(asciidoc.trim()); + List sourceLines = document.getSourceLines(); + Assertions.assertThat(sourceLines) + .containsExactly("= Document Title", + "", + "== Section A", + "", + "Section A paragraph.", + "", + "=== Section A Subsection", + "", + "Section A 'subsection' paragraph."); + } + + @Test + public void should_return_source_and_source_lines_without_trailing() { + final String asciidoc = "= Document Title\n\n" + + "== Section\n\n" + + "Hello\t \n"; + + Document document = loadDocument(asciidoc); + + String source = document.getSource(); + Assertions.assertThat(source) + .isEqualTo("= Document Title\n\n== Section\n\nHello"); + List sourceLines = document.getSourceLines(); + Assertions.assertThat(sourceLines) + .containsExactly("= Document Title", + "", + "== Section", + "", + "Hello"); + } + + @Test + public void should_return_source_and_source_lines_without_resolving_attributes() { + final String asciidoc = "= Document Title\n" + + ":an-attribute: a-value\n\n" + + "This is {an-attribute}"; + + Document document = loadDocument(asciidoc); + + String source = document.getSource(); + Assertions.assertThat(source) + .isEqualTo("= Document Title\n:an-attribute: a-value\n\nThis is {an-attribute}"); + List sourceLines = document.getSourceLines(); + Assertions.assertThat(sourceLines) + .containsExactly("= Document Title", + ":an-attribute: a-value", + "", + "This is {an-attribute}"); + } + + @Test + public void should_return_source_and_source_lines_without_resolving_includes() { + final String asciidoc = "= Document Title\n\n" + + "== Section\n\n" + + "include::partial.adoc[]"; + + Document document = loadDocument(asciidoc); + + String source = document.getSource(); + Assertions.assertThat(source) + .isEqualTo("= Document Title\n\n== Section\n\ninclude::partial.adoc[]"); + List sourceLines = document.getSourceLines(); + Assertions.assertThat(sourceLines) + .containsExactly("= Document Title", + "", + "== Section", + "", + "include::partial.adoc[]"); + } + + private Document loadDocument(String source) { + Options options = Options.builder().build(); + return asciidoctor.load(source, options); + } + + static String asciidocWithSections() { + return "= Document Title\n\n" + + "== Section A\n\n" + + "Section A paragraph.\n\n" + + "=== Section A Subsection\n\n" + + "Section A 'subsection' paragraph.\n\n"; + } } diff --git a/asciidoctorj-core/src/test/java/org/asciidoctor/jruby/ast/impl/WhenDocumentIsLoaded.java b/asciidoctorj-core/src/test/java/org/asciidoctor/jruby/ast/impl/WhenDocumentIsLoaded.java deleted file mode 100644 index 8c1643d8e..000000000 --- a/asciidoctorj-core/src/test/java/org/asciidoctor/jruby/ast/impl/WhenDocumentIsLoaded.java +++ /dev/null @@ -1,119 +0,0 @@ -package org.asciidoctor.jruby.ast.impl; - -import org.asciidoctor.Asciidoctor; -import org.asciidoctor.Attributes; -import org.asciidoctor.Options; -import org.asciidoctor.ast.Document; -import org.junit.Test; - -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; - -public class WhenDocumentIsLoaded { - - private Asciidoctor asciidoctor = Asciidoctor.Factory.create(); - - @Test - public void should_return_empty_when_document_is_empty() { - assertEmptySources(loadDocument("")); - } - - private static void assertEmptySources(Document document) { - String source = document.getSource(); - assertThat(source).isEmpty(); - List sourceLines = document.getSourceLines(); - assertThat(sourceLines).isEmpty(); - } - - @Test - public void should_return_source_and_source_lines() { - final String asciidoc = asciidocSample(); - - Document document = loadDocument(asciidoc); - - String source = document.getSource(); - assertThat(source).isEqualTo(asciidoc.trim()); - List sourceLines = document.getSourceLines(); - assertThat(sourceLines) - .containsExactly("= Document Title", - "", - "== Section A", - "", - "Section A paragraph.", - "", - "=== Section A Subsection", - "", - "Section A 'subsection' paragraph."); - } - - @Test - public void should_return_source_and_source_lines_without_trailing() { - final String asciidoc = "= Document Title\n\n" + - "== Section\n\n" + - "Hello\t \n"; - - Document document = loadDocument(asciidoc); - - String source = document.getSource(); - assertThat(source).isEqualTo("= Document Title\n\n== Section\n\nHello"); - List sourceLines = document.getSourceLines(); - assertThat(sourceLines) - .containsExactly("= Document Title", - "", - "== Section", - "", - "Hello"); - } - - @Test - public void should_return_source_and_source_lines_without_resolving_attributes() { - final String asciidoc = "= Document Title\n" + - ":an-attribute: a-value\n\n" + - "This is {an-attribute}"; - - Document document = loadDocument(asciidoc); - - String source = document.getSource(); - assertThat(source).isEqualTo("= Document Title\n:an-attribute: a-value\n\nThis is {an-attribute}"); - List sourceLines = document.getSourceLines(); - assertThat(sourceLines) - .containsExactly("= Document Title", - ":an-attribute: a-value", - "", - "This is {an-attribute}"); - } - - @Test - public void should_return_source_and_source_lines_without_resolving_includes() { - final String asciidoc = "= Document Title\n\n" + - "== Section\n\n" + - "include::partial.adoc[]"; - - Document document = loadDocument(asciidoc); - - String source = document.getSource(); - assertThat(source).isEqualTo("= Document Title\n\n== Section\n\ninclude::partial.adoc[]"); - List sourceLines = document.getSourceLines(); - assertThat(sourceLines) - .containsExactly("= Document Title", - "", - "== Section", - "", - "include::partial.adoc[]"); - } - - private Document loadDocument(String source) { - Attributes attributes = Attributes.builder().build(); - Options options = Options.builder().attributes(attributes).build(); - return asciidoctor.load(source, options); - } - - static String asciidocSample() { - return "= Document Title\n\n" + - "== Section A\n\n" + - "Section A paragraph.\n\n" + - "=== Section A Subsection\n\n" + - "Section A 'subsection' paragraph.\n\n"; - } -}