From 6f70a8e7c7310eb0f84658369abe532efb578d60 Mon Sep 17 00:00:00 2001 From: Marek Smigielski Date: Tue, 8 Oct 2013 23:22:39 +0200 Subject: [PATCH] Add methods readDocumentStructure which read detail structure of rendered asciidoc document for further processing on java side. Currently it supports retrieving of id, title, style, role, attributes and content of each block into list of individual chunks. --- README.asciidoc | 172 +++++++++++++++++- .../java/org/asciidoctor/Asciidoctor.java | 43 +++++ .../java/org/asciidoctor/ContentPart.java | 77 ++++++++ .../org/asciidoctor/StructuredDocument.java | 146 +++++++++++++++ .../java/org/asciidoctor/internal/Block.java | 11 +- .../org/asciidoctor/internal/BlockImpl.java | 78 ++++++++ .../org/asciidoctor/internal/Document.java | 89 ++++----- .../internal/JRubyAsciidoctor.java | 87 ++++++++- .../WhenStructuredDocumentIsRequired.java | 172 ++++++++++++++++++ src/test/resources/contentstructure.asciidoc | 25 +++ src/test/resources/document-with-arrays.adoc | 21 +++ src/test/resources/documentblocks.asciidoc | 43 +++++ 12 files changed, 917 insertions(+), 47 deletions(-) create mode 100644 src/main/java/org/asciidoctor/ContentPart.java create mode 100644 src/main/java/org/asciidoctor/StructuredDocument.java create mode 100644 src/main/java/org/asciidoctor/internal/BlockImpl.java create mode 100644 src/test/java/org/asciidoctor/WhenStructuredDocumentIsRequired.java create mode 100644 src/test/resources/contentstructure.asciidoc create mode 100644 src/test/resources/document-with-arrays.adoc create mode 100644 src/test/resources/documentblocks.asciidoc diff --git a/README.asciidoc b/README.asciidoc index 4d74c152a..a16a1c48d 100644 --- a/README.asciidoc +++ b/README.asciidoc @@ -211,7 +211,9 @@ Options options = options().inPlace(true).attributes(attributes).get(); String render = asciidoctor.renderFile("target/test-classes/rendersample.asciidoc", options); ... ----- +---- + +=== Document Header +readDocumentHeader+ retrieve information from the header of an AsciiDoc document without parsing or rendering the entire document. This method returns an instance of +org.asciidoctor.DocumentHeader+ with all information from the header filled. @@ -255,6 +257,174 @@ System.out.println(revisionInfo.getRemark()); <7> <6> prints "1.0" <7> prints "First draft" +=== Document structure + ++readDocumentStructure+ provides easy and useful way of parsing asciidoc file into +the structured object. First of all it gathers exactly the same information as ++readDocumentHeader+ and puts it in +header+ filed of +StructuredDocument+ object. +Actual content of the file is split into separate ContentParts based on blocks of +the content. + +There are few possible use cases of using this feature, please consider +following examples: + +[source] +.AsciiDoc document with two blocks defined by section titles +---- += Sample Document + +== Section one +This is content of section one + +== Section two +And content of section two + +... +---- + +Each section defines new content part. List of all parts can be get by +getParts+ method +on +StructuredDocument+. Each part will than contain of title (ie. "Section one") and +rendered text content as html. + +[source] +.Print content of each part +---- +for (ContentPart part : document.getParts()){ + System.out.println(part.getTitle()); + System.out.println("----"); + System.out.println(part.getContent); + System.out.println("----"); +} +---- + +[source] +.AsciiDoc document with two blocks defined by styles +---- += Sample Document + +[style one] +This is content of first content part + +[[partId]] +[style two,role="partRole"] +-- +And content of second content part + +This block can be as long as you want. +-- +---- + +This way you can then use methods like getPartByStyle to retrieve particular content parts. +[source] +.Retrieve content part by style +---- +ContentPart style_two = document.getPartByStyle("style two"); +// other possible way of retrieving parts: +ContentPart style_two = document.getPartById("partId") +ContentPart style_two = document.getPartByRole("partRole") + +//and also for lists +List parts = document.getPartsByStyle("style two"); +List parts = document.getPartsByRole("partRole"); +List parts = document.getPartsByContext("open"); + +---- + +Really nice thing about it is possibility to parse images to Image object that you can use +later to embed in html page directly from your java code or manipulate in any other way. + +[source] +.Define images +---- +[Images] +image::src/some{sp}image{sp}1.JPG[TODO title1,link="link1.html"] +image::src/some{sp}image{sp}2.JPG[TODO title2,link="link2.html"] +---- + +to get a list of images defined in the document and then to process images: + +[source] +.Retrieve image information +---- +List images = document.getPartsByContext("image"); +for (ContentPart image : images){ + String src = (String) image.getAttributes().get("target"); + String alt = (String) image.getAttributes().get("alt"); + String link = (String) image.getAttributes().get("link"); +} +---- + +As of final example consider following complete use case: + +[source] +.AsciiDoc document with product definition +---- += Sample product +v1.0, 2013-10-12 +:hardbreaks: + +:price: 70 pln +:smallImage: photos/small/small_image.jpg + +[Description] +short product description + +[Images] +image::photos/image1.jpg[title] +image::photos/image2.jpg[title] + + +[Detail] +-- +Detail information about product. Note that you can use all asciidoc features here like: +.simple list +* lists +* images +* titles +* further blocks + +[role=text-center] +also you can also add css style by assigning role to the text. +-- +---- + +and the way it can be than transformed to java object: +[source] +.Java method for getting product +---- +Product product = new Product(); +product.setTitle(document.getHeader().getDocumentTitle()); +product.setPrice(new Price((String) document.getHeader().getAttributes().get("price"))); +product.setSmallImage(new Image((String)document.getHeader().getAttributes().get("smallImage"),product.getTitle())); + +product.setDescription(document.getPartByStyle("description").getContent()); + +List images = document.getPartsByContext("image"); +for (ContentPart image : images) { + Image image = new Image(); + image.setSrc((String) image.getAttributes().get("target")); + image.setAlt((String) image.getAttributes().get("alt")); + product.getImages().add(image); +} + +product.setDetail(document.getPartByStyle("detail").getContent()); +---- + +Last feature of structure document is possibility to configure how deeply should blocks +be processed. Default is one level only so if you want to have more nested structure add +STRUCTURE_MAX_LEVEL parameter to processing options. + +[source] +.Configuration of the structure document processing +---- +Map parameters = new HashMap(); +parameters.put(Asciidoctor.STRUCTURE_MAX_LEVEL, 2); +StructuredDocument document = asciidoctor.readDocumentStructure( + new File("target/test-classes/documentblocks.asciidoc"), + parameters + ); +---- + === Utilities A utility class for searching all asciidoc files present in a root folder and all its subfolders is given. In fact it finds all files that end up with _.asc_, _.asciidoc_, _.ad_ or _.adoc_. This class is +AsciiDocDirectoryWalker+. diff --git a/src/main/java/org/asciidoctor/Asciidoctor.java b/src/main/java/org/asciidoctor/Asciidoctor.java index a039c1645..37bcc4192 100644 --- a/src/main/java/org/asciidoctor/Asciidoctor.java +++ b/src/main/java/org/asciidoctor/Asciidoctor.java @@ -20,6 +20,8 @@ */ public interface Asciidoctor { + public static final String STRUCTURE_MAX_LEVEL = "STRUCTURE_MAX_LEVEL"; + /** * Parse the AsciiDoc source input into an Document {@link DocumentRuby} and * render it to the specified backend format. @@ -273,6 +275,47 @@ String[] renderFiles(Collection asciidoctorFiles, String[] renderFiles(Collection asciidoctorFiles, OptionsBuilder options); + /** + * Reads and creates structured document containing header and content chunks. + * By default it dig only one level down but it can be tweak by setting STRUCTURE_MAX_LEVEL + * option. + * + * @param filename + * to read the attributes. + * @param options + * a Hash of options to control processing (default: {}). + * @return structured document. + */ + StructuredDocument readDocumentStructure(File filename,Map options); + + /** + * Reads and creates structured document containing header and content chunks. + * By default it dig only one level down but it can be tweak by setting STRUCTURE_MAX_LEVEL + * option. + * + * @param content + * where rendered content is written. Writer is flushed, but not + * closed. + * @param options + * a Hash of options to control processing (default: {}). + * @return structured document. + */ + StructuredDocument readDocumentStructure(String content,Map options); + + /** + * Reads and creates structured document containing header and content chunks. + * By default it dig only one level down but it can be tweak by setting STRUCTURE_MAX_LEVEL + * option. + * + * @param contentReader + * where asciidoc content is read. + * @param options + * a Hash of options to control processing (default: {}). + * @return structured document. + */ + StructuredDocument readDocumentStructure(Reader contentReader,Map options); + + /** * Reads only header parameters instead of all document. * diff --git a/src/main/java/org/asciidoctor/ContentPart.java b/src/main/java/org/asciidoctor/ContentPart.java new file mode 100644 index 000000000..d9413c2a8 --- /dev/null +++ b/src/main/java/org/asciidoctor/ContentPart.java @@ -0,0 +1,77 @@ +package org.asciidoctor; + +import java.util.List; +import java.util.Map; + +public class ContentPart { + private String id; + private int level; + private String context; + private String style; + private String role; + private String title; + private Map attributes; + private String content; + private List parts; + + private ContentPart() { + super(); + } + + public String getId() { + return id; + } + + public int getLevel() { + return level; + } + + public String getContext() { + return context; + } + + public String getStyle() { + return style; + } + + public String getRole() { + return role; + } + + public String getTitle() { + return title; + } + + public Map getAttributes() { + return attributes; + } + + public String getContent() { + return content; + } + + public List getParts() { + return parts; + } + + public void setParts(List parts) { + this.parts = parts; + } + + public static ContentPart createContentPart(String id, int level,String context, String title, + String style, String role, Map attributes, + String textContent) { + ContentPart contentPart = new ContentPart(); + contentPart.level=level; + contentPart.id=id; + contentPart.context=context; + contentPart.style=style; + contentPart.role=role; + contentPart.title=title; + contentPart.attributes=attributes; + contentPart.content=textContent; + + return contentPart; + } + +} diff --git a/src/main/java/org/asciidoctor/StructuredDocument.java b/src/main/java/org/asciidoctor/StructuredDocument.java new file mode 100644 index 000000000..26aa70800 --- /dev/null +++ b/src/main/java/org/asciidoctor/StructuredDocument.java @@ -0,0 +1,146 @@ +package org.asciidoctor; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Structure document containing header and content chunks + * + * @author marek + * + */ +public class StructuredDocument { + + private DocumentHeader header; + + private List parts; + + private StructuredDocument() { + super(); + } + + public List getParts() { + return parts; + } + + public DocumentHeader getHeader() { + return header; + } + + /** + * Return first matched part by id + * + * @param id + * @return + */ + public ContentPart getPartById(String id) { + if (id != null) { + for (ContentPart part : parts) { + if (id.equals(part.getId())) { + return part; + } + } + } + return null; + } + + /** + * Return first matched part by style + * + * @param style + * @return + */ + public ContentPart getPartByStyle(String style) { + if (style != null) { + for (ContentPart part : parts) { + if (style.equals(part.getStyle())) { + return part; + } + } + } + return null; + } + + /** + * Return first matched part by role + * + * @param style + * @return + */ + public ContentPart getPartByRole(String role) { + if (role != null) { + for (ContentPart part : parts) { + if (role.equals(part.getRole())) { + return part; + } + } + } + return null; + } + /** + * Return all parts that match specified context + * + * @param style + * @return + */ + public List getPartsByContext(String context) { + if (context != null) { + List filteredParts = new ArrayList(); + for (ContentPart part : parts) { + if (context.equals(part.getContext())) { + filteredParts.add(part); + } + } + return filteredParts; + } + return Collections.EMPTY_LIST; + } + + /** + * Return all parts that match specified style + * + * @param style + * @return + */ + public List getPartsByStyle(String style) { + if (style != null) { + List filteredParts = new ArrayList(); + for (ContentPart part : parts) { + if (style.equals(part.getStyle())) { + filteredParts.add(part); + } + } + return filteredParts; + } + return Collections.EMPTY_LIST; + } + + /** + * Return all parts that match specified role + * + * @param style + * @return + */ + public List getPartsByRole(String role) { + if (role != null) { + List filteredParts = new ArrayList(); + for (ContentPart part : parts) { + if (role.equals(part.getRole())) { + filteredParts.add(part); + } + } + return filteredParts; + } + return Collections.EMPTY_LIST; + } + + public static StructuredDocument createStructuredDocument( + DocumentHeader header, List parts) { + StructuredDocument document = new StructuredDocument(); + document.header = header; + document.parts = parts; + + return document; + } +} diff --git a/src/main/java/org/asciidoctor/internal/Block.java b/src/main/java/org/asciidoctor/internal/Block.java index a4f950631..3f56475ac 100644 --- a/src/main/java/org/asciidoctor/internal/Block.java +++ b/src/main/java/org/asciidoctor/internal/Block.java @@ -5,9 +5,16 @@ public interface Block { - String context(); - List lines(); + String id(); + String title(); + String role(); + String style(); + List blocks(); Map attributes(); + Object content(); + String render(); + String context(); + List lines(); } diff --git a/src/main/java/org/asciidoctor/internal/BlockImpl.java b/src/main/java/org/asciidoctor/internal/BlockImpl.java new file mode 100644 index 000000000..7c1b2fbe7 --- /dev/null +++ b/src/main/java/org/asciidoctor/internal/BlockImpl.java @@ -0,0 +1,78 @@ +package org.asciidoctor.internal; + +import java.util.List; +import java.util.Map; + +import org.jruby.Ruby; +import org.jruby.RubyArray; +import org.jruby.RubyObject; + +public class BlockImpl implements Block{ + private Block blockRuby; + private Ruby rubyRuntime; + + public BlockImpl(Block blockRuby, Ruby rubyRuntime) { + this.blockRuby = blockRuby; + this.rubyRuntime = rubyRuntime; + } + + @Override + public String id() { + return blockRuby.id(); + } + + @Override + public String title() { + return blockRuby.title(); + } + + @Override + public String role() { + return blockRuby.role(); + } + + @Override + public String style() { + return blockRuby.style(); + } + + @Override + public List blocks() { + List rubyBlocks = this.blockRuby.blocks(); + + for (int i = 0; i < rubyBlocks.size(); i++) { + if (!(rubyBlocks.get(i) instanceof RubyArray) && !(rubyBlocks.get(i) instanceof Block)) { + Block blockRuby = RubyUtils.rubyToJava(rubyRuntime, + (RubyObject) rubyBlocks.get(i), Block.class); + rubyBlocks.set(i, new BlockImpl(blockRuby, rubyRuntime)); + } + } + + return rubyBlocks; + } + + @Override + public Map attributes() { + return blockRuby.attributes(); + } + + @Override + public Object content() { + return blockRuby.content(); + } + + @Override + public String render() { + return blockRuby.render(); + } + + @Override + public String context() { + return blockRuby.context(); + } + + @Override + public List lines() { + return blockRuby.lines(); + } +} diff --git a/src/main/java/org/asciidoctor/internal/Document.java b/src/main/java/org/asciidoctor/internal/Document.java index 560d761c8..4d5bea5ef 100644 --- a/src/main/java/org/asciidoctor/internal/Document.java +++ b/src/main/java/org/asciidoctor/internal/Document.java @@ -4,51 +4,56 @@ import java.util.Map; import org.jruby.Ruby; +import org.jruby.RubyArray; import org.jruby.RubyObject; public class Document implements DocumentRuby { - private DocumentRuby documentRuby; - private Ruby rubyRuntime; - - public Document(DocumentRuby documentRuby, Ruby rubyRuntime) { - this.documentRuby = documentRuby; - this.rubyRuntime = rubyRuntime; - } - - public DocumentRuby getDocumentRuby() { - return documentRuby; - } - - @Override - public String doctitle() { - return this.documentRuby.doctitle(); - } - - @Override - public String title() { - return this.documentRuby.title(); - } - - @Override - public Map getAttributes() { - return this.documentRuby.getAttributes(); - } - - @Override - public boolean basebackend(String backend) { - return this.documentRuby.basebackend(backend); - } - - @Override - public List blocks() { - List rubyBlocks = this.documentRuby.blocks(); - - for (int i = 0; i < rubyBlocks.size(); i++) { - rubyBlocks.set(i, RubyUtils.rubyToJava(rubyRuntime, (RubyObject)rubyBlocks.get(i), Block.class)); - } - - return rubyBlocks; - } + private DocumentRuby documentRuby; + private Ruby rubyRuntime; + + public Document(DocumentRuby documentRuby, Ruby rubyRuntime) { + this.documentRuby = documentRuby; + this.rubyRuntime = rubyRuntime; + } + + public DocumentRuby getDocumentRuby() { + return documentRuby; + } + + @Override + public String doctitle() { + return this.documentRuby.doctitle(); + } + + @Override + public String title() { + return this.documentRuby.title(); + } + + @Override + public Map getAttributes() { + return this.documentRuby.getAttributes(); + } + + @Override + public boolean basebackend(String backend) { + return this.documentRuby.basebackend(backend); + } + + @Override + public List blocks() { + List rubyBlocks = this.documentRuby.blocks(); + + for (int i = 0; i < rubyBlocks.size(); i++) { + if (!(rubyBlocks.get(i) instanceof RubyArray) && !(rubyBlocks.get(i) instanceof Block)) { + Block blockRuby = RubyUtils.rubyToJava(rubyRuntime, + (RubyObject) rubyBlocks.get(i), Block.class); + rubyBlocks.set(i, new BlockImpl(blockRuby, rubyRuntime)); + } + } + + return rubyBlocks; + } } diff --git a/src/main/java/org/asciidoctor/internal/JRubyAsciidoctor.java b/src/main/java/org/asciidoctor/internal/JRubyAsciidoctor.java index 37f607bc4..f31d40977 100644 --- a/src/main/java/org/asciidoctor/internal/JRubyAsciidoctor.java +++ b/src/main/java/org/asciidoctor/internal/JRubyAsciidoctor.java @@ -12,10 +12,12 @@ import java.util.Map; import org.asciidoctor.Asciidoctor; +import org.asciidoctor.ContentPart; import org.asciidoctor.DirectoryWalker; import org.asciidoctor.DocumentHeader; import org.asciidoctor.Options; import org.asciidoctor.OptionsBuilder; +import org.asciidoctor.StructuredDocument; import org.asciidoctor.extension.JavaExtensionRegistry; import org.asciidoctor.extension.RubyExtensionRegistry; import org.jruby.CompatVersion; @@ -28,6 +30,8 @@ public class JRubyAsciidoctor implements Asciidoctor { private static final String GEM_PATH = "GEM_PATH"; + + private static final int DEFAULT_MAX_LEVEL = 1; private AsciidoctorModule asciidoctorModule; protected RubyGemsPreloader rubyGemsPreloader; @@ -96,8 +100,87 @@ private static DocumentHeader toDocumentHeader(DocumentRuby documentRuby) { documentRuby.title(), documentRuby.getAttributes()); } - @Override - public DocumentHeader readDocumentHeader(File filename) { + private StructuredDocument toDocument(DocumentRuby documentRuby, + Ruby rubyRuntime, int maxDeepLevel) { + + Document document = new Document(documentRuby, rubyRuntime); + List contentParts = getContents(document.blocks(),1,maxDeepLevel); + return StructuredDocument.createStructuredDocument( + toDocumentHeader(documentRuby), + contentParts); + } + + + private List getContents(List blocks, int level, int maxDeepLevel){ + //finish getting childs if max structure level was riched + if (level>maxDeepLevel){ + 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 parts = new ArrayList(); + for (Block block : blocks) { + parts.add(getContentPartFromBlock(block,level,maxDeepLevel)); + } + return parts; + } + + private ContentPart getContentPartFromBlock(Block child, int level, int maxDeepLevel) { + Object content = child.content(); + String textContent; + if (content instanceof String) { + textContent = (String) content; + } else { + textContent = child.render(); + } + ContentPart contentPart = ContentPart.createContentPart(child.id(), level, child.context(), child.title(), + child.style(), child.role(), child.attributes(), textContent); + contentPart.setParts(getContents(child.blocks(),level+1,maxDeepLevel)); + return contentPart; + } + + @Override + public StructuredDocument readDocumentStructure(File filename, + Map options) { + + this.rubyGemsPreloader.preloadRequiredLibraries(options); + + RubyHash rubyHash = RubyHashUtil.convertMapToRubyHashWithSymbols( + rubyRuntime, options); + DocumentRuby documentRuby = this.asciidoctorModule.load_file( + filename.getAbsolutePath(), rubyHash); + int maxDeepLevel = options.containsKey(STRUCTURE_MAX_LEVEL)?(Integer) (options.get(STRUCTURE_MAX_LEVEL)):DEFAULT_MAX_LEVEL; + return toDocument(documentRuby, rubyRuntime, maxDeepLevel); + } + + @Override + public StructuredDocument readDocumentStructure(String content, + Map options) { + + this.rubyGemsPreloader.preloadRequiredLibraries(options); + + RubyHash rubyHash = RubyHashUtil.convertMapToRubyHashWithSymbols( + rubyRuntime, options); + + DocumentRuby documentRuby = this.asciidoctorModule.load(content, + rubyHash); + int maxDeepLevel = options.containsKey(STRUCTURE_MAX_LEVEL)?(Integer) (options.get(STRUCTURE_MAX_LEVEL)):DEFAULT_MAX_LEVEL; + return toDocument(documentRuby, rubyRuntime, maxDeepLevel); + } + + @Override + public StructuredDocument readDocumentStructure(Reader contentReader, + Map options) { + String content = IOUtils.readFull(contentReader); + return readDocumentStructure(content, options); + } + + @Override + public DocumentHeader readDocumentHeader(File filename) { RubyHash rubyHash = getParseHeaderOnlyOption(); diff --git a/src/test/java/org/asciidoctor/WhenStructuredDocumentIsRequired.java b/src/test/java/org/asciidoctor/WhenStructuredDocumentIsRequired.java new file mode 100644 index 000000000..d93aa4247 --- /dev/null +++ b/src/test/java/org/asciidoctor/WhenStructuredDocumentIsRequired.java @@ -0,0 +1,172 @@ +package org.asciidoctor; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.io.File; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.asciidoctor.internal.JRubyAsciidoctor; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +public class WhenStructuredDocumentIsRequired { + + @Rule + public TemporaryFolder testFolder = new TemporaryFolder(); + + private Asciidoctor asciidoctor = JRubyAsciidoctor.create(); + + @Test + public void structural_content_should_be_retrieved_from_file() { + + Map parameters = new HashMap(); + parameters.put(Asciidoctor.STRUCTURE_MAX_LEVEL, 2); + StructuredDocument document = asciidoctor.readDocumentStructure( + new File("target/test-classes/documentblocks.asciidoc"),parameters + ); + + DocumentHeader header = document.getHeader(); + + assertThat(header.getDocumentTitle(), is("Sample Document")); + assertThat(header.getPageTitle(), is("Sample Document")); + + Map attributes = header.getAttributes(); + assertThat((String) attributes.get("revdate"), is("2013-05-20")); + assertThat((String) attributes.get("revnumber"), is("1.0")); + assertThat((String) attributes.get("revremark"), is("First draft")); + assertThat((String) attributes.get("tags"), is("[document, example]")); + assertThat((String) attributes.get("author"), is("Doc Writer")); + assertThat((String) attributes.get("email"), + is("doc.writer@asciidoc.org")); + + List parts = document.getParts(); + + Assert.assertNotNull(parts); + Assert.assertEquals(3, parts.size()); + + Assert.assertEquals("section", parts.get(0).getContext()); + Assert.assertEquals("Abstract", parts.get(0).getTitle()); + + Assert.assertEquals(1, parts.get(0).getParts().size()); + Assert.assertEquals("abstract", parts.get(0).getParts().get(0).getStyle()); + Assert.assertEquals("open", parts.get(0).getParts().get(0).getContext()); + + Assert.assertEquals("First Section", parts.get(1).getTitle()); + Assert.assertEquals("_first_section", parts.get(1).getId()); + Assert.assertEquals("section", parts.get(1).getContext()); + Assert.assertEquals(5, parts.get(1).getParts().size()); + + + Assert.assertEquals("blockid", parts.get(1).getParts().get(1).getId()); + Assert.assertEquals("blockStyle", parts.get(1).getParts().get(1).getStyle()); + Assert.assertEquals("Abraham Lincoln", parts.get(1).getParts().get(1).getAttributes() + .get("attribution")); + + Assert.assertEquals("feature-list", parts.get(1).getParts().get(2).getRole()); + + Assert.assertEquals("Second Section", parts.get(2).getTitle()); + Assert.assertEquals(2, parts.get(2).getParts().size()); + + Assert.assertEquals("image", parts.get(2).getParts().get(0).getContext()); + Assert.assertEquals("image", parts.get(2).getParts().get(1).getContext()); + } + + @Test + public void some_real_content(){ + StructuredDocument document = asciidoctor.readDocumentStructure( + new File("target/test-classes/contentstructure.asciidoc"), + new HashMap()); + + DocumentHeader header = document.getHeader(); + + assertThat(header.getDocumentTitle(), is("TODO: Document Title")); + + Map attributes = header.getAttributes(); + assertThat((String) attributes.get("type"), is("object.type")); + + List parts = document.getParts(); + + Assert.assertNotNull(parts); + Assert.assertEquals(4, parts.size()); + + + + Assert.assertEquals("TODO: This is description", document.getPartByStyle("Description").getContent()); + + List images = document.getPartsByContext("image"); + int imageCount = 2; + Assert.assertEquals(imageCount, images.size()); + for (int i = 1; i<=imageCount; i++) { + Assert.assertEquals("src/some image "+i+".JPG",(String) images.get(i-1).getAttributes().get("target")); + Assert.assertEquals("TODO title"+i,(String) images.get(i-1).getAttributes().get("alt")); + Assert.assertEquals("link"+i+".html",(String) images.get(i-1).getAttributes().get("link")); + } + Assert.assertTrue(document.getPartByStyle("Text").getContent().startsWith("
")); + } + + @Test + public void title_should_be_retrieved_from_simple_string() { + + StructuredDocument document = asciidoctor.readDocumentStructure( + "= Sample Document", new HashMap()); + + DocumentHeader header = document.getHeader(); + + assertThat(header.getDocumentTitle(), is("Sample Document")); + + List parts = document.getParts(); + + Assert.assertNotNull(parts); + Assert.assertEquals(0, parts.size()); + + } + + @Test + public void one_part_should_be_retrieved_from_simple_string() { + + StructuredDocument document = asciidoctor.readDocumentStructure( + "Simple single paragraph", new HashMap()); + + DocumentHeader header = document.getHeader(); + + Assert.assertNotNull(header); + + List parts = document.getParts(); + + Assert.assertNotNull(parts); + Assert.assertEquals(1, parts.size()); + Assert.assertEquals("Simple single paragraph", parts.get(0) + .getContent()); + + } + + @Test + public void no_parts_should_be_retrieved_from_empty_string() { + + StructuredDocument document = asciidoctor.readDocumentStructure( + "", new HashMap()); + + DocumentHeader header = document.getHeader(); + + Assert.assertNotNull(header); + + List parts = document.getParts(); + + Assert.assertNotNull(parts); + Assert.assertEquals(0, parts.size()); + } + + @Test + public void test_problematic_document() { + StructuredDocument document = asciidoctor.readDocumentStructure( + new File("target/test-classes/document-with-arrays.adoc"), + new HashMap()); + } + + +} diff --git a/src/test/resources/contentstructure.asciidoc b/src/test/resources/contentstructure.asciidoc new file mode 100644 index 000000000..a9b5e982d --- /dev/null +++ b/src/test/resources/contentstructure.asciidoc @@ -0,0 +1,25 @@ += TODO: Document Title +v1.0, 2013-10-12 + +:type: object.type + +[Description] +TODO: This is description + +[Images] +image::src/some{sp}image{sp}1.JPG[TODO title1,link="link1.html"] +image::src/some{sp}image{sp}2.JPG[TODO title2,link="link2.html"] + +[Text] +-- +[role=text-center] +some text with attributes + +image::src/image_in_text.jpg + +[role=text-center] +and more text + +[role=text-center] +and even more text +-- \ No newline at end of file diff --git a/src/test/resources/document-with-arrays.adoc b/src/test/resources/document-with-arrays.adoc new file mode 100644 index 000000000..73efa237f --- /dev/null +++ b/src/test/resources/document-with-arrays.adoc @@ -0,0 +1,21 @@ += Asciidoctor Changelog + +http://asciidoctor.org[Asciidoctor] is an open source text processor and publishing toolchain for converting http://asciidoctor.org[AsciiDoc] markup into HTML, DocBook and custom formats. + +This document provides a high-level view of the changes introduced in Asciidoctor by release. +For a detailed view of what has changed, refer to the https://github.com/asciidoctor/asciidoctor/commits/master[commit history] on GitHub. + +== 0.1.4 (2013-09-05) - @mojavelinux + +Performance:: + + * 15% increase in speed compared to 0.1.3 + +Enhancements:: + + * updated xref inline macro to support inter-document references (#417) + + +Bug Fixes:: + + * lowercase attribute names passed to API (#508) \ No newline at end of file diff --git a/src/test/resources/documentblocks.asciidoc b/src/test/resources/documentblocks.asciidoc new file mode 100644 index 000000000..bb32ff14c --- /dev/null +++ b/src/test/resources/documentblocks.asciidoc @@ -0,0 +1,43 @@ += Sample Document +Doc Writer ; John Smith +v1.0, 2013-05-20: First draft +:title: Sample Document +:tags: [document, example] + +== Abstract +[abstract] +A chunk of content that can be used where the abstract of the content needs to be displayed. + +== First Section +The abstract is a built-in role (called a style). When a built-in role is not available, a role can be used to pass the same type of information: + +.Block title +[[blockid]] +[blockStyle, attribution="Abraham Lincoln", Soldiers' National Cemetery Dedication] +This is some section with title and id and attributes + +[role="feature-list"] +* lightweight +* reusable +* readable +* etc... + +And not only can a role be assigned to a single paragraph, block or list, it can also be assigned to a sequence of content thanks to the open block. + +[role="lead"] +-- +Opening paragraph of the lead. + +Another lead paragraph. + +* topic 1 +* topic 2 +* topic 3 + +Conclusion of the lead. +-- +== Second Section + +[Images] +image::sunset.jpg[Sunset] +image::sunset2.jpg[Sunset2] \ No newline at end of file