Fixes Add factory methods to create lists
MODIFY
CHANGELOG.adoc
CHANGELOG.adoc
@@ -20,6 +20,10 @@ Improvement::
* Upgrade to Asciidoctor EPUB3 1.5.0-alpha.16
* Upgrade to Asciidoctor Diagram 2.0.1
+Bug Fixes::
+
+ * Add missing factory methods to create Lists (@glisicivan) (#916)
+
Build::
* Upgrade jruby-gradle-plugin to 2.0.0-alpha.7 and load Gems directly from rubygems.org instead of using torquebox Maven proxy.

MODIFY
asciidoctorj-api/src/main/java/org/asciidoctor/extension/BaseProcessor.java
asciidoctorj-api/src/main/java/org/asciidoctor/extension/BaseProcessor.java
@@ -129,6 +129,21 @@ public class BaseProcessor implements Processor {
return delegate.createListItem(parent, text);
}
+ @Override
+ public org.asciidoctor.ast.List createList(StructuralNode parent, String context) {
+ return delegate.createList(parent, context);
+ }
+
+ @Override
+ public org.asciidoctor.ast.List createList(StructuralNode parent, String context, Map<String, Object> attributes, Map<Object, Object> options) {
+ return delegate.createList(parent, context, attributes, options);
+ }
+
+ @Override
+ public org.asciidoctor.ast.List createList(StructuralNode parent, String context, Map<Object, Object> options) {
+ return delegate.createList(parent, context);
+ }
+
@Override
public ListItem createListItem(DescriptionList parent, String text) {
return delegate.createListItem(parent, text);

MODIFY
asciidoctorj-api/src/main/java/org/asciidoctor/extension/Contexts.java
asciidoctorj-api/src/main/java/org/asciidoctor/extension/Contexts.java
@@ -156,6 +156,45 @@ public @interface Contexts {
*/
String PARAGRAPH = ":paragraph";
+ /**
+ * Predefined constant for making a Processor work on ordered lists.
+ *
+ * <pre>
+ * 1. First item
+ * 2. Second item
+ * </pre>
+ */
+ String OLIST = ":olist";
+
+ /**
+ * Predefined constant for making a Processor work on unordered lists.
+ *
+ * <pre>
+ * . First item
+ * . Second item
+ * </pre>
+ */
+ String ULIST = ":ulist";
+
+ /**
+ * Predefined constant for making a Processor work on unordered lists.
+ *
+ * <pre>
+ * . First item
+ * . Second item
+ * </pre>
+ */
+ String COLIST = ":colist";
+
+ /**
+ * Predefined constant for making a Processor work on unordered lists.
+ *
+ * <pre>
+ * First:: The first item
+ * Second:: The second item
+ * </pre>
+ */
+ String DLIST = ":dlist";
String[] value();

MODIFY
asciidoctorj-api/src/main/java/org/asciidoctor/extension/Processor.java
asciidoctorj-api/src/main/java/org/asciidoctor/extension/Processor.java
@@ -81,6 +81,18 @@ public interface Processor {
*/
Document createDocument(Document parentDocument);
+ /**
+ * Creates a new List
+ * @param parent The block to which the parsed content should be added as children.
+ * @param context Either {@code "olist"}, {@code ulist}, {@code colist} or {@code dlist}
+ * @return
+ */
+ org.asciidoctor.ast.List createList(StructuralNode parent, String context);
+
+ org.asciidoctor.ast.List createList(StructuralNode parent, String context, Map<String, Object> attributes, Map<Object, Object> options);
+
+ org.asciidoctor.ast.List createList(StructuralNode parent, String context, Map<Object, Object> options);
+
ListItem createListItem(final org.asciidoctor.ast.List parent, final String text);
ListItem createListItem(final org.asciidoctor.ast.DescriptionList parent, final String text);

MODIFY
asciidoctorj-core/src/main/java/org/asciidoctor/jruby/extension/internal/JRubyProcessor.java
asciidoctorj-core/src/main/java/org/asciidoctor/jruby/extension/internal/JRubyProcessor.java
@@ -8,21 +8,21 @@ import org.asciidoctor.ast.ContentModel;
import org.asciidoctor.ast.ContentNode;
import org.asciidoctor.ast.Document;
import org.asciidoctor.ast.ListItem;
-import org.asciidoctor.jruby.ast.impl.NodeConverter;
import org.asciidoctor.ast.PhraseNode;
import org.asciidoctor.ast.Row;
import org.asciidoctor.ast.Section;
import org.asciidoctor.ast.StructuralNode;
import org.asciidoctor.ast.Table;
+import org.asciidoctor.extension.Processor;
+import org.asciidoctor.extension.Reader;
import org.asciidoctor.jruby.ast.impl.ColumnImpl;
import org.asciidoctor.jruby.ast.impl.ContentNodeImpl;
import org.asciidoctor.jruby.ast.impl.DescriptionListImpl;
import org.asciidoctor.jruby.ast.impl.DocumentImpl;
import org.asciidoctor.jruby.ast.impl.ListImpl;
+import org.asciidoctor.jruby.ast.impl.NodeConverter;
import org.asciidoctor.jruby.ast.impl.RowImpl;
import org.asciidoctor.jruby.ast.impl.StructuralNodeImpl;
-import org.asciidoctor.extension.Processor;
-import org.asciidoctor.extension.Reader;
import org.asciidoctor.jruby.internal.JRubyAsciidoctor;
import org.asciidoctor.jruby.internal.JRubyRuntimeContext;
import org.asciidoctor.jruby.internal.RubyHashUtil;
@@ -291,6 +291,38 @@ public class JRubyProcessor implements Processor {
return (Document) NodeConverter.createASTNode(runtime, NodeConverter.NodeType.DOCUMENT_CLASS, runtime.getNil(), options);
}
+
+ @Override
+ public org.asciidoctor.ast.List createList(StructuralNode parent, String context) {
+ return createList(parent, context, new HashMap<>(), new HashMap<>());
+
+ }
+
+ @Override
+ public org.asciidoctor.ast.List createList(StructuralNode parent, String context,
+ Map<String, Object> attributes,
+ Map<Object, Object> options) {
+
+ options.put(Options.ATTRIBUTES, new HashMap<>(attributes));
+ return createList(parent, context, options);
+ }
+
+ @Override
+ public org.asciidoctor.ast.List createList(StructuralNode parent, String context,
+ Map<Object, Object> options) {
+
+ Ruby rubyRuntime = JRubyRuntimeContext.get(parent);
+
+ RubyHash convertMapToRubyHashWithSymbols = RubyHashUtil.convertMapToRubyHashWithSymbolsIfNecessary(rubyRuntime,
+ filterBlockOptions(parent, options, "subs", ContentModel.KEY));
+
+ IRubyObject[] parameters = {
+ ((StructuralNodeImpl) parent).getRubyObject(),
+ RubyUtils.toSymbol(rubyRuntime, context),
+ convertMapToRubyHashWithSymbols};
+ return (org.asciidoctor.ast.List) NodeConverter.createASTNode(rubyRuntime, NodeConverter.NodeType.LIST_CLASS, parameters);
+ }
+
@Override
public ListItem createListItem(final org.asciidoctor.ast.List parent, final String text) {
Ruby rubyRuntime = JRubyRuntimeContext.get(parent);

ADD
asciidoctorj-core/src/test/groovy/org/asciidoctor/extension/ListCreatorBlockMacro.groovy
+package org.asciidoctor.extension
+
+
+import org.asciidoctor.ast.StructuralNode
+
+@Name('list')
+class ListCreatorBlockMacro extends BlockMacroProcessor {
+
+ String context
+
+ ListCreatorBlockMacro(String context) {
+ this.context = context
+ }
+
+ @Override
+ Object process(StructuralNode parent, String target, Map<String, Object> attributes) {
+
+ def attrs = new HashMap<String, Object>()
+ attrs['start'] = '42'
+ def opts = new HashMap<Object, Object>()
+
+ org.asciidoctor.ast.List list = createList(parent, context, attrs, opts)
+
+ list.getBlocks().add(createListItem(list, 'First item'))
+ list.getBlocks().add(createListItem(list, 'Second item'))
+
+ list
+ }
+}

ADD
asciidoctorj-core/src/test/groovy/org/asciidoctor/extension/WhenABlockMacroProcessorCreatesAList.groovy
+package org.asciidoctor.extension
+
+import org.asciidoctor.Asciidoctor
+import org.asciidoctor.OptionsBuilder
+import org.asciidoctor.SafeMode
+import org.jboss.arquillian.spock.ArquillianSputnik
+import org.jboss.arquillian.test.api.ArquillianResource
+import org.jsoup.Jsoup
+import org.jsoup.nodes.Document
+import org.junit.runner.RunWith
+import spock.lang.Specification
+
+@RunWith(ArquillianSputnik)
+class WhenABlockMacroProcessorCreatesAList extends Specification {
+
+ public static final String LISTMACRO_NAME = 'list'
+ public static final String UTF_8 = 'UTF-8'
+ public static final String OL = 'ol'
+ public static final String UL = 'ul'
+ public static final String LI = 'li'
+ public static final String FIRST_ITEM = 'First item'
+ public static final String SECOND_ITEM = 'Second item'
+
+ @ArquillianResource
+ private Asciidoctor asciidoctor
+
+ private static final String DOCUMENT = '''
+= Section Creation Test
+
+list::HelloWorld[]
+'''
+
+ def "the ordered list should appear in the resulting document"() {
+
+ given:
+ asciidoctor.javaExtensionRegistry().blockMacro(LISTMACRO_NAME, new ListCreatorBlockMacro('olist'))
+
+ when:
+ String result = asciidoctor.convert(DOCUMENT, OptionsBuilder.options().safe(SafeMode.SAFE).toFile(false).headerFooter(false))
+
+ then:
+ noExceptionThrown()
+ Document htmlDocument = Jsoup.parse(result, UTF_8)
+
+ htmlDocument.select(OL).size() == 1
+ def ol = htmlDocument.select(OL).first()
+ ol.attr('start') == '42'
+ def items = ol.select(LI)
+ items.size() == 2
+ items.get(0).text() == FIRST_ITEM
+ items.get(1).text() == SECOND_ITEM
+ }
+
+ def "the unordered list should appear in the resulting document"() {
+
+ given:
+ asciidoctor.javaExtensionRegistry().blockMacro(LISTMACRO_NAME, new ListCreatorBlockMacro('ulist'))
+
+ when:
+ String result = asciidoctor.convert(DOCUMENT, OptionsBuilder.options().safe(SafeMode.SAFE).toFile(false).headerFooter(false))
+
+ then:
+ noExceptionThrown()
+ Document htmlDocument = Jsoup.parse(result, UTF_8)
+
+ htmlDocument.select(UL).size() == 1
+ def ul = htmlDocument.select(UL).first()
+ def items = ul.select(LI)
+ items.size() == 2
+ items.get(0).text() == FIRST_ITEM
+ items.get(1).text() == SECOND_ITEM
+ }
+}

MODIFY
config/codenarc/codenarc.groovy
config/codenarc/codenarc.groovy
@@ -53,5 +53,9 @@ ruleset {
exclude 'UnnecessaryGetter'
exclude 'UnnecessaryCollectCall'
}
- ruleset('rulesets/dry.xml')
+ ruleset('rulesets/dry.xml') {
+ 'DuplicateNumberLiteral' {
+ ignoreNumbers = '0,1,2'
+ }
+ }
}

Fixes #916. Add factory methods to create lists