Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple small changes #509

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't tested it, but I don't think this will compile.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does, though. Why shouldn't it?

Copy link
Member

@robertpanzer robertpanzer Sep 8, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha! :-)
Could you please add braces around the return null?

I probably automatically put them in there mentally around both returns that have the same indentation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

}

@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"));
}
}