Fixes Use StructuralNode.level for ContentPart API instead of recursion level
MODIFY
asciidoctorj-core/src/main/java/org/asciidoctor/internal/JRubyAsciidoctor.java
asciidoctorj-core/src/main/java/org/asciidoctor/internal/JRubyAsciidoctor.java
@@ -178,31 +178,25 @@ public class JRubyAsciidoctor implements Asciidoctor, LogHandler {
private StructuredDocument toDocument(Document document, Ruby rubyRuntime, int maxDeepLevel) {
Document documentImpl = (Document) NodeConverter.createASTNode(document);
- List<ContentPart> contentParts = getContents(documentImpl.getBlocks(), 1, maxDeepLevel);
+ List<ContentPart> contentParts = getContents(documentImpl.getBlocks(), maxDeepLevel);
return StructuredDocumentImpl.createStructuredDocument(toDocumentHeader(documentImpl), contentParts);
}
- private List<ContentPart> getContents(List<StructuralNode> blocks, int level, int maxDeepLevel) {
- // finish getting childs if max structure level was riched
- if (level > maxDeepLevel) {
+ private List<ContentPart> getContents(List<StructuralNode> blocks, int maxDeepLevel) {
+ if (blocks == null) {
return null;
}
- // if document has only one child don't treat as actual contentpart
- // unless
- // it has no childs
- /*
- * if (blocks.size() == 1 && blocks.get(0).blocks().size() > 0) { return getContents(blocks.get(0).blocks(), 0,
- * maxDeepLevel); }
- */
- // add next level of contentParts
- List<ContentPart> parts = new ArrayList<ContentPart>();
+ List<ContentPart> parts = new ArrayList<>();
for (StructuralNode block : blocks) {
- parts.add(getContentPartFromBlock(block, level, maxDeepLevel));
+ if (block.getLevel() > maxDeepLevel) {
+ continue;
+ }
+ parts.add(getContentPartFromBlock(block, maxDeepLevel));
}
return parts;
}
- private ContentPart getContentPartFromBlock(StructuralNode child, int level, int maxDeepLevel) {
+ private ContentPart getContentPartFromBlock(StructuralNode child, int maxDeepLevel) {
Object content = child.getContent();
String textContent;
if (content instanceof String) {
@@ -210,9 +204,9 @@ public class JRubyAsciidoctor implements Asciidoctor, LogHandler {
} else {
textContent = child.convert();
}
- ContentPartImpl contentPart = ContentPartImpl.createContentPart(child.getId(), level, child.getContext(), child.getTitle(),
+ ContentPartImpl contentPart = ContentPartImpl.createContentPart(child.getId(), child.getLevel(), child.getContext(), child.getTitle(),
child.getStyle(), child.getRole(), child.getAttributes(), textContent);
- contentPart.setParts(getContents(child.getBlocks(), level + 1, maxDeepLevel));
+ contentPart.setParts(getContents(child.getBlocks(), maxDeepLevel));
return contentPart;
}
@@ -278,7 +272,7 @@ public class JRubyAsciidoctor implements Asciidoctor, LogHandler {
}
private RubyHash getParseHeaderOnlyOption() {
- Map<String, Object> options = new HashMap<String, Object>();
+ Map<String, Object> options = new HashMap<>();
options.put("parse_header_only", true);
return RubyHashUtil.convertMapToRubyHashWithSymbols(rubyRuntime, options);
}

ADD
asciidoctorj-core/src/test/groovy/org/asciidoctor/WhenTheSectionLevelIsAccessed.groovy
+package org.asciidoctor
+
+import org.asciidoctor.ast.Section
+import org.asciidoctor.internal.JRubyAsciidoctor
+import org.asciidoctor.util.ClasspathResources
+import org.jboss.arquillian.spock.ArquillianSputnik
+import org.jboss.arquillian.test.api.ArquillianResource
+import org.junit.Rule
+import org.junit.runner.RunWith
+import spock.lang.Specification
+
+/**
+ * Tests that the unsubstituted text can be retrieved from nodes
+ */
+@RunWith(ArquillianSputnik)
+class WhenTheSectionLevelIsAccessed extends Specification {
+
+ public static final String DOC_TITLE = 'Include other files'
+ public static final String TITLE_FIRST_CHAPTER = 'First chapter'
+ public static final String TITLE_FIRST_SUBSECTION = 'More First'
+ public static final String TITLE_SECOND_CHAPTER = 'Second chapter'
+
+ public static final String FILENAME_FLAT = 'parts/mainflat.adoc'
+ public static final String FILENAME_WITH_INCLUDES = 'parts/main.adoc'
+
+ public static final int ONE = 1
+ public static final int TWO = 2
+ public static final int THREE = 3
+ public static final String CONTEXT_PARAGRAPH = 'paragraph'
+
+ @ArquillianResource
+ private Asciidoctor asciidoctor
+
+ @Rule
+ ClasspathResources classpath = new ClasspathResources()
+
+ def 'it should return the correct level from the ContentPart API'() {
+
+ when:
+ def doc = asciidoctor.readDocumentStructure(classpath.getResource(FILENAME_FLAT),
+ OptionsBuilder.options()
+ .safe(SafeMode.UNSAFE)
+ .option(JRubyAsciidoctor.STRUCTURE_MAX_LEVEL, THREE)
+ .asMap())
+
+ then:
+ doc.parts.size() == TWO
+ doc.parts[0].level == ONE
+ doc.parts[0].title == TITLE_FIRST_CHAPTER
+ doc.parts[0].parts.size() == TWO
+ def subsection = doc.parts[0].parts[ONE]
+ subsection.level == THREE
+ subsection.title == TITLE_FIRST_SUBSECTION
+ subsection.parts.size() == ONE
+ doc.parts[ONE].level == ONE
+ doc.parts[ONE].title == TITLE_SECOND_CHAPTER
+ doc.parts[ONE].parts.size() == ONE
+ }
+
+ def 'it should not recurse deeper than max level'() {
+
+ when:
+ def doc = asciidoctor.readDocumentStructure(classpath.getResource(FILENAME_FLAT),
+ OptionsBuilder.options()
+ .safe(SafeMode.UNSAFE)
+ .option(JRubyAsciidoctor.STRUCTURE_MAX_LEVEL, TWO)
+ .asMap())
+
+ then:
+ doc.parts.size() == TWO
+ doc.parts[0].level == ONE
+ doc.parts[0].title == TITLE_FIRST_CHAPTER
+ doc.parts[0].parts.size() == ONE
+ def paragraph = doc.parts[0].parts[0]
+ paragraph.level == ONE
+ paragraph.title == null
+ paragraph.parts == null || paragraph.parts.empty
+ paragraph.context == CONTEXT_PARAGRAPH
+ doc.parts[ONE].level == ONE
+ doc.parts[ONE].title == TITLE_SECOND_CHAPTER
+ doc.parts[ONE].parts.size() == ONE
+ }
+
+ def 'it should return the correct level from the ContentPart API with leveloffset'() {
+
+ when:
+ def doc = asciidoctor.readDocumentStructure(classpath.getResource(FILENAME_WITH_INCLUDES),
+ OptionsBuilder.options()
+ .safe(SafeMode.UNSAFE)
+ .option(JRubyAsciidoctor.STRUCTURE_MAX_LEVEL, THREE)
+ .asMap())
+
+ then:
+ doc.parts.size() == TWO
+ doc.parts[0].level == ONE
+ doc.parts[0].title == TITLE_FIRST_CHAPTER
+ doc.parts[0].parts.size() == TWO
+ def subsection = doc.parts[0].parts[ONE]
+ subsection.level == THREE
+ subsection.title == TITLE_FIRST_SUBSECTION
+ subsection.parts.size() == ONE
+ doc.parts[ONE].level == ONE
+ doc.parts[ONE].title == TITLE_SECOND_CHAPTER
+ doc.parts[ONE].parts.size() == ONE
+ }
+
+ def 'it should not recurse deeper than max level with level offset'() {
+
+ when:
+ def doc = asciidoctor.readDocumentStructure(classpath.getResource(FILENAME_WITH_INCLUDES),
+ OptionsBuilder.options()
+ .safe(SafeMode.UNSAFE)
+ .option(JRubyAsciidoctor.STRUCTURE_MAX_LEVEL, TWO)
+ .asMap())
+
+ then:
+ doc.parts.size() == TWO
+ doc.parts[0].level == ONE
+ doc.parts[0].title == TITLE_FIRST_CHAPTER
+ doc.parts[0].parts.size() == ONE
+ def paragraph = doc.parts[0].parts[0]
+ paragraph.level == ONE
+ paragraph.title == null
+ paragraph.parts == null || paragraph.parts.empty
+ paragraph.context == CONTEXT_PARAGRAPH
+ doc.parts[ONE].level == ONE
+ doc.parts[ONE].title == TITLE_SECOND_CHAPTER
+ doc.parts[ONE].parts.size() == ONE
+ }
+
+
+ def 'it should return the correct level from the AST API'() {
+
+
+ when:
+ def doc = asciidoctor.loadFile(classpath.getResource(FILENAME_WITH_INCLUDES),
+ OptionsBuilder.options().safe(SafeMode.UNSAFE).asMap())
+
+ then:
+ doc.level == 0
+ doc.title == DOC_TITLE
+ doc.blocks.size() == TWO
+ def section1 = doc.blocks[0] as Section
+ section1.level == ONE
+ section1.title == TITLE_FIRST_CHAPTER
+ section1.blocks.size() == TWO
+ section1.blocks[ONE].level == THREE
+ section1.blocks[ONE].title == TITLE_FIRST_SUBSECTION
+ def section2 = doc.blocks[ONE] as Section
+ section2.level == ONE
+ section2.title == TITLE_SECOND_CHAPTER
+ section2.blocks.size() == ONE
+ }
+
+}

ADD
asciidoctorj-core/src/test/resources/parts/chapter1.adoc
+= First chapter
+
+Sample document to be included.
+
+=== More First
+
+Some more
\ No newline at end of file

ADD
asciidoctorj-core/src/test/resources/parts/chapter2.adoc
+=== Second chapter
+
+Sample document to be included.

ADD
asciidoctorj-core/src/test/resources/parts/main.adoc
+= Include other files
+:sectnums:
+
+:leveloffset: +1
+
+include::chapter1.adoc[]
+
+:leveloffset: -1
+
+include::chapter2.adoc[leveloffset=-1]

ADD
asciidoctorj-core/src/test/resources/parts/mainflat.adoc
+= Include other files
+:sectnums:
+
+== First chapter
+
+Sample document to be included.
+
+==== More First
+
+Some more
+
+== Second chapter
+
+Sample document to be included.

Fixes #681. Use StructuralNode.level for ContentPart API instead of recursion level