From adc3c9a985be2942f27db0121839077132b0a87c Mon Sep 17 00:00:00 2001 From: Robert Panzer Date: Sat, 23 Jul 2016 15:59:46 +0200 Subject: [PATCH] Added method Document.getCounter. Interaction with using counters in the Document not possible yet --- .../org/asciidoctor/ast/ContentModel.java | 12 +- .../java/org/asciidoctor/ast/Document.java | 19 ++ .../asciidoctor/ast/impl/DocumentImpl.java | 10 + .../WhenAJavaExtensionUsesCounters.groovy | 185 ++++++++++++++++++ 4 files changed, 220 insertions(+), 6 deletions(-) create mode 100644 asciidoctorj-core/src/test/groovy/org/asciidoctor/extension/WhenAJavaExtensionUsesCounters.groovy diff --git a/asciidoctorj-core/src/main/java/org/asciidoctor/ast/ContentModel.java b/asciidoctorj-core/src/main/java/org/asciidoctor/ast/ContentModel.java index 5707855c6..715f86117 100644 --- a/asciidoctorj-core/src/main/java/org/asciidoctor/ast/ContentModel.java +++ b/asciidoctorj-core/src/main/java/org/asciidoctor/ast/ContentModel.java @@ -53,32 +53,32 @@ /** * Predefined constant to let Asciidoctor know that this BlockProcessor creates simple paragraph content. */ - public static final String SIMPLE =":simple"; + public static final String SIMPLE = ":simple"; /** * Predefined constant to let Asciidoctor know that this BlockProcessor creates literal content. */ - public static final String VERBATIM =":verbatim"; + public static final String VERBATIM = ":verbatim"; /** * Predefined constant to make Asciidoctor pass through the content unprocessed. */ - public static final String RAW =":raw"; + public static final String RAW = ":raw"; /** * Predefined constant to make Asciidoctor drop the content. */ - public static final String SKIP =":skip"; + public static final String SKIP = ":skip"; /** * Predefined constant to make Asciidoctor not expect any content. */ - public static final String EMPTY =":empty"; + public static final String EMPTY = ":empty"; /** * Predefined constant to make Asciidoctor parse content as attributes. */ - public static final String ATTRIBUTES =":attributes"; + public static final String ATTRIBUTES = ":attributes"; /** * See the constants defined in this enumeration for possible values. diff --git a/asciidoctorj-core/src/main/java/org/asciidoctor/ast/Document.java b/asciidoctorj-core/src/main/java/org/asciidoctor/ast/Document.java index 0861b566d..b7fc1229e 100644 --- a/asciidoctorj-core/src/main/java/org/asciidoctor/ast/Document.java +++ b/asciidoctorj-core/src/main/java/org/asciidoctor/ast/Document.java @@ -58,4 +58,23 @@ public interface Document extends StructuralNode { * @return options defined in document. */ Map getOptions(); + + /** + * Gets the current counter with the given name and increases its value. + * At the first invocation the counter will return 1. + * After the call the value of the counter is set to the returned value plus 1. + * @param name + * @return + */ + int getAndIncrementCounter(String name); + + /** + * Gets the current counter with the given name and increases its value. + * At the first invocation the counter will return the given initial value. + * After the call the value of the counter is set to the returned value plus 1. + * @param name + * @param initialValue + * @return + */ + int getAndIncrementCounter(String name, int initialValue); } diff --git a/asciidoctorj-core/src/main/java/org/asciidoctor/ast/impl/DocumentImpl.java b/asciidoctorj-core/src/main/java/org/asciidoctor/ast/impl/DocumentImpl.java index 6ef1be9b0..12d2165c1 100644 --- a/asciidoctorj-core/src/main/java/org/asciidoctor/ast/impl/DocumentImpl.java +++ b/asciidoctorj-core/src/main/java/org/asciidoctor/ast/impl/DocumentImpl.java @@ -54,4 +54,14 @@ public String doctitle() { return getDoctitle(); } + @Override + public int getAndIncrementCounter(String name) { + return getInt("counter", name); + } + + @Override + public int getAndIncrementCounter(String name, int initialValue) { + return getInt("counter", name, initialValue); + } + } diff --git a/asciidoctorj-core/src/test/groovy/org/asciidoctor/extension/WhenAJavaExtensionUsesCounters.groovy b/asciidoctorj-core/src/test/groovy/org/asciidoctor/extension/WhenAJavaExtensionUsesCounters.groovy new file mode 100644 index 000000000..adb9d184e --- /dev/null +++ b/asciidoctorj-core/src/test/groovy/org/asciidoctor/extension/WhenAJavaExtensionUsesCounters.groovy @@ -0,0 +1,185 @@ +package org.asciidoctor.extension + +import groovy.transform.CompileStatic +import org.asciidoctor.Asciidoctor +import org.asciidoctor.OptionsBuilder +import org.asciidoctor.SafeMode +import org.asciidoctor.ast.Block +import org.asciidoctor.ast.ContentModel +import org.asciidoctor.ast.Section +import org.asciidoctor.ast.StructuralNode +import org.jboss.arquillian.spock.ArquillianSputnik +import org.jboss.arquillian.test.api.ArquillianResource +import org.jsoup.Jsoup +import org.jsoup.nodes.Document +import org.jsoup.select.Elements +import org.junit.runner.RunWith +import spock.lang.Specification + +import static org.asciidoctor.ast.ContentModel.SIMPLE + +@SuppressWarnings('DuplicateNumberLiteral') +@RunWith(ArquillianSputnik) +class WhenAJavaExtensionUsesCounters extends Specification { + + public static final String UTF8 = 'UTF-8' + + public static final String PARAGRAPH = 'paragraph' + + public static final String PARAGRAPH_SELECTOR = '.paragraph' + + @ArquillianResource + private Asciidoctor asciidoctor + + + def 'using the counter macro should continue the counters set in macros but doesnt'() { + given: + String document = '''= Test document + +== Test section + +testmacro::countera[] + +testmacro::counterb[] + +testmacro::countera[] + +The attribute for countera: {counter:countera} + +The attribute for counterb: {counter:counterb} + +''' + when: + asciidoctor.javaExtensionRegistry().blockMacro(TestBlockMacroProcessor) + String result = asciidoctor.convert(document, OptionsBuilder.options().headerFooter(true).safe(SafeMode.SERVER)) + + then: + Document doc = Jsoup.parse(result, UTF8) + Elements paragraphs = doc.select(PARAGRAPH_SELECTOR) + paragraphs.get(0).text() == 'This is macro call 1 for countera' + paragraphs.get(1).text() == 'This is macro call 1 for counterb' + paragraphs.get(2).text() == 'This is macro call 2 for countera' + + paragraphs.get(3).text() == 'The attribute for countera: 1' + paragraphs.get(4).text() == 'The attribute for counterb: 1' + } + + def 'using a treeprocessor should continue the counters set in macros'() { + given: + String document = '''= Test document + +== Test section + +[.count] +countera + +[.count] +counterb + +The attribute for countera: {counter:countera} + +The attribute for counterb: {counter:counterb} + +''' + when: + asciidoctor.javaExtensionRegistry().treeprocessor(TestTreeProcessor) + String result = asciidoctor.convert(document, OptionsBuilder.options().headerFooter(true).safe(SafeMode.SERVER)) + + then: + Document doc = Jsoup.parse(result, UTF8) + Elements paragraphs = doc.select(PARAGRAPH_SELECTOR) + paragraphs.get(0).text() == 'Counter countera has value 1' + paragraphs.get(1).text() == 'Counter counterb has value 1' + paragraphs.get(2).text() == 'The attribute for countera: 2' + paragraphs.get(3).text() == 'The attribute for counterb: 2' + } + + def 'an initial seed should be used'() { + given: + String document = '''= Test document + +== Test section + +testmacro::countera[] + +testmacro::counterb[] + +testmacro::countera[] + +''' + when: + asciidoctor.javaExtensionRegistry().blockMacro(TestBlockMacroWithInitialCounterProcessor) + String result = asciidoctor.convert(document, OptionsBuilder.options().headerFooter(true).safe(SafeMode.SERVER)) + + then: + Document doc = Jsoup.parse(result, UTF8) + Elements paragraphs = doc.select(PARAGRAPH_SELECTOR) + paragraphs.get(0).text() == 'This is macro call 42 for countera' + paragraphs.get(1).text() == 'This is macro call 42 for counterb' + paragraphs.get(2).text() == 'This is macro call 43 for countera' + } + + @CompileStatic + @Name('testmacro') + @Contexts(Contexts.CONTEXT_PARAGRAPH) + @ContentModel(SIMPLE) + static class TestBlockMacroProcessor extends BlockMacroProcessor { + + @Override + Object process(StructuralNode parent, String target, Map attributes) { + + String text = "This is macro call ${parent.document.getAndIncrementCounter(target)} for ${target}" + // Have to do this to interact successfully with counters also used in the document + // String text = "This is macro call {counter:${target}} for ${target}" + + createBlock(parent, PARAGRAPH, text) + } + } + + @CompileStatic + @Name('testmacro') + @Contexts(Contexts.CONTEXT_PARAGRAPH) + @ContentModel(SIMPLE) + static class TestBlockMacroWithInitialCounterProcessor extends BlockMacroProcessor { + + @Override + Object process(StructuralNode parent, String target, Map attributes) { + + String text = "This is macro call ${parent.document.getAndIncrementCounter(target, 42)} for ${target}" + + createBlock(parent, PARAGRAPH, text) + } + } + + static class TestTreeProcessor extends Treeprocessor { + + @Override + org.asciidoctor.ast.Document process(org.asciidoctor.ast.Document document) { + + List newNodes = document.blocks.collect { + if (it instanceof Section) { + processSection((Section) it) + } else { + it + } + } + document.blocks.clear() + document.blocks.addAll(newNodes) + + document + } + + Section processSection(Section section) { + List newNodes = section.blocks.collect { + if (it instanceof Block && it.getRoles().contains('count')) { + createBlock(section, PARAGRAPH, "Counter ${it.content} has value ${it.document.getAndIncrementCounter(it.source)}") + } else { + it + } + } + section.blocks.clear() + section.blocks.addAll(newNodes) + section + } + } +}