Skip to content

Commit

Permalink
Multiple small changes (#509)
Browse files Browse the repository at this point in the history
* Ignore bin/ directory produced by Eclipse.

* Recursively convert nested maps to ruby hashes.

* Convert nested Java maps to Ruby hashes by assuming the keys are strings.

* Return null if the inner document of a table cell is nil.

* Add getter for @content_model variable of AbstractBlock.

* Add test for content_model getter on StructuralNode.

* Add test case for accessing the inner document of table cells that do not have an inner document.

* Add test case for avoiding a ClassCastException when accessing the attributes of a block that was previously created by a block processor.

* Formatting.

* Fix a few code style problems.
  • Loading branch information
kduske-n4 authored and robertpanzer committed Sep 9, 2016
1 parent 07fed40 commit 543ad9d
Show file tree
Hide file tree
Showing 8 changed files with 196 additions and 30 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
bin/
build/
target/
.classpath
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,15 @@ public interface StructuralNode extends ContentNode {
List<StructuralNode> findBy(Map<Object, Object> selector);
int getLevel();

/**
* Returns the content model.
*
* @see ContentModel
*
* @return the content model
*/
String getContentModel();

/**
* Returns the source location of this block.
* This information is only available if the {@code sourcemap} option is enabled when loading or rendering the document.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,11 @@ public void setVerticalAlignment(Table.VerticalAlignment valign) {

@Override
public Document getInnerDocument() {
return (Document) NodeConverter.createASTNode(getRubyProperty("inner_document"));
IRubyObject innerDocument = getRubyProperty("inner_document");
if (innerDocument.isNil()) {
return null;
}
return (Document) NodeConverter.createASTNode(innerDocument);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class StructuralNodeImpl extends ContentNodeImpl implements StructuralNod

private static final String BLOCK_CLASS = "Block";
private static final String SECTION_CLASS = "Section";

public StructuralNodeImpl(IRubyObject blockDelegate) {
super(blockDelegate);
}
Expand Down Expand Up @@ -91,6 +91,11 @@ public Cursor getSourceLocation() {
return new CursorImpl(object);
}

@Override
public String getContentModel() {
return getString("content_model");
}

@Override
public List<String> getSubstitutions() {
return getList("subs", String.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ private static IRubyObject toRubyObject(Ruby rubyRuntime, Object value) {

if (value instanceof List) {
return toRubyArray(rubyRuntime, (List<Object>) value);
} else if (value instanceof Map) {
return convertMapToRubyHashWithStrings(rubyRuntime, (Map<String, Object>) value);
} else {

if (isNotARubySymbol(value)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import org.asciidoctor.ast.Document
import org.asciidoctor.ast.Table
import org.jboss.arquillian.spock.ArquillianSputnik
import org.jboss.arquillian.test.api.ArquillianResource
import org.jruby.exceptions.RaiseException
import org.junit.runner.RunWith

import spock.lang.Specification

@RunWith(ArquillianSputnik)
Expand Down Expand Up @@ -55,4 +57,33 @@ class WhenATableIsLoaded extends Specification {
tableNode.body[0].cells[0].colspan == 0
}

def "asking a table cell for its inner document when it does not have one should return null"() {

given:
String document = '''= Test document
[cols="40,60"]
|===
| Source | Output
a|
The first content cell
a|
The second content cell
|===
'''

when:
Document documentNode = asciidoctor.load(document, OptionsBuilder.options().headerFooter(false).asMap())
Table tableNode = documentNode.blocks[0]

then:
tableNode.header[0].cells[0].innerDocument == null
tableNode.header[0].cells[1].innerDocument == null
notThrown(RaiseException)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package org.asciidoctor.extension

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.Document
import org.asciidoctor.ast.StructuralNode
import org.jboss.arquillian.spock.ArquillianSputnik
import org.jboss.arquillian.test.api.ArquillianResource
import org.junit.runner.RunWith

import spock.lang.Specification

@RunWith(ArquillianSputnik)
class WhenABlockProcessorCreatesABlockThatATreeProcessorVisits extends Specification {

@Name('tst')
@Contexts(Contexts.CONTEXT_OPEN)
@ContentModel(ContentModel.COMPOUND)
static class BlockCreator extends BlockProcessor {

@Override
Object process(StructuralNode parent, Reader reader, Map<String, Object> attributes) {
List<String> output = new LinkedList<>()
output.add('line 1')
output.add('line 2')

attributes.put('name', 'value')

createBlock(parent, 'open', output, attributes)
}
}

static class BlockVisitor extends Treeprocessor {

@Override
Document process(Document document) {
recurse(document)
document
}

private void recurse(StructuralNode node) {
// Accessing the attributes of a block that was previously created by a block processor
// causes a ClassCastException in ContentNodeImpl#getAttributes since the value returned
// from the AbstractNode in the JRuby AST is an instance of MapJavaProxy, which does not
// conform to RubyHash.
Map<String, Object> attributes = node.getAttributes()

// To silence Codenarc. We must access the attributes to provoke the error.
attributes = new HashMap<String, Object>(attributes)

for (Block block : node.getBlocks()) {
recurse(block)
}
}
}

@ArquillianResource
private Asciidoctor asciidoctor

private static final String DOCUMENT = '''
= Block and Tree Processor Interaction Test
[tst]
--
This will be ignored
--
'''

def "execution should not throw class cast exception"() {

given:
asciidoctor.javaExtensionRegistry().block(BlockCreator)
asciidoctor.javaExtensionRegistry().treeprocessor(BlockVisitor)

when:
asciidoctor.convert(DOCUMENT, OptionsBuilder.options().safe(SafeMode.SAFE).toFile(false).headerFooter(true))

then:
notThrown(ClassCastException)
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -29,25 +29,25 @@
@RunWith(Arquillian.class)
public class WhenAsciiDocIsRenderedToDocument {

private static final String DOCUMENT = "= Document Title\n" +
"\n" +
"preamble\n" +
"\n" +
"== Section A\n" +
"\n" +
"paragraph\n" +
"\n" +
"--\n" +
"Exhibit A::\n" +
"+\n" +
"[#tiger.animal]\n" +
"image::tiger.png[Tiger]\n" +
"--\n" +
"\n" +
"image::cat.png[Cat]\n" +
"\n" +
"== Section B\n" +
"\n" +
private static final String DOCUMENT = "= Document Title\n" +
"\n" +
"preamble\n" +
"\n" +
"== Section A\n" +
"\n" +
"paragraph\n" +
"\n" +
"--\n" +
"Exhibit A::\n" +
"+\n" +
"[#tiger.animal]\n" +
"image::tiger.png[Tiger]\n" +
"--\n" +
"\n" +
"image::cat.png[Cat]\n" +
"\n" +
"== Section B\n" +
"\n" +
"paragraph";

private static final String ROLE = "[\"quote\", \"author\", \"source\", role=\"famous\"]\n" +
Expand All @@ -74,34 +74,34 @@ public void should_return_section_blocks() {
assertThat(section.sectname(), is("sect1"));
assertThat(section.special(), is(false));
}

@Test
public void should_return_blocks_from_a_document() {

Document document = asciidoctor.load(DOCUMENT, new HashMap<String, Object>());
assertThat(document.doctitle(), is("Document Title"));

}

@Test
public void should_return_a_document_object_from_string() {

Document document = asciidoctor.load(DOCUMENT, new HashMap<String, Object>());
assertThat(document.doctitle(), is("Document Title"));
}

@Test
public void should_find_elements_from_document() {

Document document = asciidoctor.load(DOCUMENT, new HashMap<String, Object>());
Map<Object, Object> selector = new HashMap<Object, Object>();
selector.put("context", ":image");
List<StructuralNode> findBy = document.findBy(selector);
assertThat(findBy, hasSize(2));

assertThat((String)findBy.get(0).getAttributes().get("target"), is("tiger.png"));
assertThat(findBy.get(0).getLevel(), greaterThan(0));

}

@Test
Expand Down Expand Up @@ -309,4 +309,31 @@ public void should_get_attributes() {
assertThat(section.hasAttr("docattr", false), is(false));
}

@Test
public void should_get_content_model() {
final String documentWithPreambleAndSection = ""
+ "= Document Title\n"
+ "\n"
+ "A test document with a preamble and a section.\n"
+ "\n"
+ "The preamble contains multiple paragraphs to force the outer block to be compound.\n"
+ "\n"
+ "== First Section\n"
+ "\n"
+ "And herein lies the problem.\n"
+ "\n";

Document document = asciidoctor.load(documentWithPreambleAndSection, new HashMap<String, Object>());
List<StructuralNode> blocks = document.getBlocks();

StructuralNode preambleContainer = blocks.get(0);
assertThat(preambleContainer.getContentModel(), is("compound"));

assertThat(preambleContainer.getBlocks().get(0).getContentModel(), is("simple"));
assertThat(preambleContainer.getBlocks().get(1).getContentModel(), is("simple"));

Section section = (Section) blocks.get(1);
assertThat(section.getContentModel(), is("compound"));
assertThat(section.getBlocks().get(0).getContentModel(), is("simple"));
}
}

0 comments on commit 543ad9d

Please sign in to comment.