anchor.diff:  ----
   List<ContentPart> 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");
  +  String src = (String) image.getAttributes().get("target");
  +  String alt = (String) image.getAttributes().get("alt");
  +  String link = (String) image.getAttributes().get("link");
   }
   ----
   
  
positive.code: = AsciidoctorJ: Asciidoctor Java integration & API
  Alex Soto
  :asciidoc-uri: http://asciidoc.org
  :asciidoctor-uri: http://asciidoctor.org
  :source-language: java
  :language: {source-language}
  :icons: font
  
  ifdef::env-github[]
  [link=https://travis-ci.org/asciidoctor/asciidoctorj]
  image::http://img.shields.io/travis/asciidoctor/asciidoctorj/master.svg[Build Status, link="https://travis-ci.org/asciidoctor/asciidoctorj"]
  endif::[]
  
  AsciidoctorJ is the official means of using {asciidoctor-uri}[Asciidoctor] to render all your {asciidoc-uri}[AsciiDoc] documentation using Java instead of Ruby.
  
  == Installation
  
  Since AsciidoctorJ is a standard jar file, the only thing you should do is add library into classpath.
  
  .Dependency declaration in Maven
  [source,xml]
  ----
  ...
  <dependencies>
    <dependency>
      <groupId>org.asciidoctor</groupId>
      <artifactId>asciidoctorj</artifactId>
      <version>${asciidoctor.version}</version>  <!--1-->
      ...
    </dependency>
  </dependencies>
  ...
  ----
  
  .Dependency declaration in SBT
  [source,scala]
  ----
  ...
  libraryDependencies += "org.asciidoctor" % "asciidoctorj" % "${asciidoctor.version}" //<1>
  ...
  ----
  
  .Dependency declaration in Gradle
  [source,groovy]
  ----
  ...
  dependencies {
      compile('org.asciidoctor:asciidoctorj:${asciidoctor.version}') {  //<1>
          transitive = false
      }
  }
  ...
  ----
  <1> As this library tracks the version of asciidoctor, you can use whichever version of asciidoctor you prefer.
  
  == Usage
  
  The core interface of AsciidoctorJ is the +Asciidoctor+ interface. It provides two methods for rendering asciidoc content, +render+ and +renderFile+. Both of them returns a string with rendered content.
  
  .Method description
  [cols="1m,2"]
  |===
  |Name |Description
  
  |render
  |Parse the AsciiDoc content into a Document and render it to the specified backend format.
  
  |renderFile
  |Parse the content of AsciiDoc file into a Document and render it to the specified backend format.
  |===
  
  Also a factory method is provided to create an instance of +Asciidoctor+ interface.
  
  .Creation of Asciidoctor interface
  [source]
  ----
  import static org.asciidoctor.Asciidoctor.Factory.create;
  import org.asciidoctor.Asciidoctor;
  ...
  Asciidoctor asciidoctor = create();
  ...
  ----
  
  And then we can call +render+ methods depending on our requirements.
  
  .Rendering a String
  [source]
  ----
  ...
  String rendered = asciidoctor.render("*This* is it.", Collections.EMPTY_MAP);
  System.out.println(rendered);
  ...
  ----
  
  But also you can render the content of a file.
  
  .Rendering a File
  [source]
  ----
  ...
  String rendered = asciidoctor.renderFile(new File("target/test-classes/rendersample.asciidoc"), Collections.EMPTY_MAP);
  System.out.println(rendered);
  ...
  ----
  
  Or a list of Asciidoc files:
  
  .Rendering a list of files.
  [source]
  ----
  ...
  String[] allRenderedFiles = asciidoctor.renderFiles(Arrays.asList(new File("target/test-classes/rendersample.asciidoc")), options);
  ...
  ----
  
  If the rendered content is not written into files, +renderDirectory+ will return an array listing all the documents rendered.
  
  Another method provided by +Asciidoctor+ interface is +renderDirectory+. This method renders all AsciiDoc files (+_.asc_+, +_.asciidoc_+, +_.ad_+ or +_.adoc_+), that are present inside provided folder or any of its subfolder.
  
  In case rendered content is not written in files, this method returns an array with all documents rendered.
  
  .Rendering all files of directory
  [source]
  ----
  ...
  String[] allRenderedFiles = asciidoctor.renderDirectory(new File("target/test-classes/src"), new HashMap<String, Object>());
  
  for(String renderedFile:allRenderedFiles) {
      System.out.println(renderedFile);
  }
  ...
  ----
  
  Another way to render AsciiDoc content is by calling +render+ method but providing a +Reader+ and +Writer+. +Reader+ interface is used as source, and rendered content is written through +Writer+ interface.
  
  .Rendering content to a Writer
  [source]
  ----
  ...
  FileReader inputAsciidoctorFile = new FileReader(new File("target/test-classes/rendersample.asciidoc"));
  StringWriter rendererWriter = new StringWriter();
  
  asciidoctor.render(inputAsciidoctorFile, rendererWriter, options().asMap());
  
  StringBuffer renderedContent = rendererWriter.getBuffer();
  assertRenderedFile(renderedContent.toString());
  ...
  ----
  
  === Options
  
  {asciidoctor-uri}[Asciidoctor] supports different kind of options, like +in_place+ which renders the output inside a file, +template_dir+ used to provide a directory of Tilt-compatible templates to be used instead of the default built-in templates, or for example +attributes+ option where we can set key-value pairs of attributes that will be used within asciidoc document.
  
  The second parameter of +render+ methods are a +java.util.Map+ where all these options can be set.
  
  .Example of using in_place Option and backend Attribute
  [source]
  ----
  Map<String, Object> attributes = new HashMap<String, Object>();
  attributes.put("backend", "docbook");
  
  Map<String, Object> options = new HashMap<String, Object>();
  options.put("in_place", true);
  options.put("attributes", attributes);
  
  String render = asciidoctor.renderFile("target/test-classes/rendersample.asciidoc", options);
  ----
  
  See that in previous example we have created a Map, where we have put the options and attributes (creating a Map too) required to render input as docbook and generate an output file.
  
  Another way for setting options is by using +org.asciidoctor.Options+ class. +Options+ is a simple Java class which contains methods for setting required options. Note that related with +org.asciidoctor.Options+ class, there is +org.asciidoctor.Attributes+ class, which can be used for setting attributes.
  
  +render+ methods are overloaded, so we can pass +org.asciidoctor.Options+ as parameter instead of +java.util.Map+.
  
  .Example of using Options and Attributes class instead of Map.
  [source]
  ----
  Attributes attributes = new Attributes();
  attributes.setBackend("docbook");
  
  Options options = new Options();
  options.setInPlace(true);
  options.setAttributes(attributes);
  
  String render = asciidoctor.renderFile("target/test-classes/rendersample.asciidoc", options);
  
  ...
  ----
  
  But AsciidoctorJ also provides two fluent interfaces to create these maps and classes in a more readable form.
  
  +org.asciidoctor.AttributesBuilder+ is provided for creating required attributes set, and +org.asciidoctor.OptionsBuilder+ can be used for options. Previous example but using these classes looks like:
  
  .Example setting attributes and options with map
  [source]
  ----
  import static org.asciidoctor.AttributesBuilder.attributes;
  import static org.asciidoctor.OptionsBuilder.options;
  
  ...
  
  Map<String, Object> attributes = attributes().backend("docbook").asMap();
  Map<String, Object> options = options().inPlace(true).attributes(attributes).asMap();
  
  String render = asciidoctor.renderFile("target/test-classes/rendersample.asciidoc", options);
  
  ...
  ----
  
  .Example setting attributes and options with classes
  [source]
  ----
  import static org.asciidoctor.AttributesBuilder.attributes;
  import static org.asciidoctor.OptionsBuilder.options;
  
  ...
  
  Attributes attributes = attributes().backend("docbook").get();
  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.
  
  .AsciiDoc document with header information
  [source]
  ----
  = Sample Document
  Doc Writer <doc.writer@asciidoc.org>; John Smith <john.smith@asciidoc.org>
  
negative.code:     parameters);
  ----
  
  === JRuby wrapping classes
  
  [source]
  ----
  import org.asciidoctor.ast.Document;
  
  //...
  
  Document document = asciidoctor.load(DOCUMENT, new HashMap<String, Object>()); // <1>
  assertThat(document.doctitle(), is("Document Title")); // <2>
  ----
  <1> Document from an String is loaded into `Document` object.
  <2> Title of the document is retrieved.
  
  But also all blocks that conforms the document can be retrieved.
  Currently there are support for three kinds of blocks.
  `Blocks` itself, `Section` for sections of the document and `AbstractBlock` which is the base type where all kind of blocks (including those not mapped as Java class) are mapped.
  
  [source]
  ----
  import org.asciidoctor.ast.Document;
  import org.asciidoctor.ast.Section;
  
  //...
  
  Document document = asciidoctor.load(DOCUMENT, new HashMap<String, Object>()); // <1>
  Section section = (Section) document.blocks().get(1); // <2>
  
  assertThat(section.index(), is(0)); // <3>
  assertThat(section.sectname(), is("sect1"));
  assertThat(section.special(), is(false));
  ----
  <1> Document from an String is loaded into `Document` object.
  <2> All blocks are get and because in this example the first block is a Section block, we cast it directly.
  <3> Concrete methods for sections can be called.
  
  Blocks can also be retrieved from query using `findBy` method.
  
  [source]
  ----
  Document document = asciidoctor.load(DOCUMENT, new HashMap<String, Object>());
  Map<Object, Object> selector = new HashMap<Object, Object>(); // <1>
  selector.put("context", ":image"); // <2>
  
  List<AbstractBlock> findBy = document.findBy(selector);
  assertThat(findBy, hasSize(2)); //<3>
  ----
  <1> To make queries you need to use a `map` approach. Currently this is because of the Asciidoctor API but it will change in near future.
  <2> In this example all blocks with context as image is returned. Notice that the colon (`:`) must be added in the value part.
  <3> Document used as example contains two images.
  
  == Extension API
  
  One of the major improvements to Asciidoctor recently is the extensions API.
  AsciidoctorJ brings this extension API to the JVM environment.
  {uri-repo}[AsciidoctorJ] allows us to write extensions in Java instead of Ruby.
  
  Asciidoctor provides seven types of extension points.
  Each extension point has an abstract class in Java that maps to the extension API in Ruby.
  
  [cols="1m,2"]
  .AsciidoctorJ extension APIs
  |===
  |Name |Class
  
  |Preprocessor
  |org.asciidoctor.extension.Preprocessor
  
  |Treeprocessor
  |org.asciidoctor.extension.Treeprocessor
  
  |Postprocessor
  |org.asciidoctor.extension.Postprocessor
  
  |Block processor
  |org.asciidoctor.extension.BlockProcessor
  
  |Block macro processor
  |org.asciidoctor.extension.BlockMacroProcessor
  
  |Inline macro processor
  |org.asciidoctor.extension.InlineMacroProcessor
  
  |Include processor
  |org.asciidoctor.extension.IncludeProcessor
  |===
  
  To create an extension two things are required:
  
  . Create a class implementing an extension class (this will depend on the kind of interface being developed)
  . Register your class using the `JavaExtensionRegistry` class
  //^
  
  // Is this list a sublist of the list above?
  
  An extension can be registered by:
  
  * Passing as String a fully qualified class.
  * Passing a Class object of the extension.
  * Passing an instance of the extension object.
  
  NOTE: In the first two cases, the lifecycle of the instance is managed by JRuby.
  In the last case, the caller is the owner of the lifeycle of the object.
  
  TIP: Using an already created instance as an extension is useful when using CDI.
  For example, you can inject any extension inside the code and then register that instance as an extension.
  
  === Preprocessor
  
  This extension updates an attribute value defined in a document.
  
  [source]
  .Preprocessor example
  ----
  public class ChangeAttributeValuePreprocessor extends Preprocessor { // <1>
  
  	public ChangeAttributeValuePreprocessor(Map<String, Object> config) { // <2>
  		super(config);
  	}
  
  	@Override
  	public PreprocessorReader process(Document document, PreprocessorReader reader) { // <3>
  		document.getAttributes().put("content", "Alex");
  		return reader;
  	}
  
  }
  ----
  <1> Class must extend from `Preprocessor`.
  <2> A constructor receiving a `Map` must be provided in case you want to send options to the preprocessor.
  <3> The `process` method receives a `Document` and `PreprocessorReader`.
  
  [source]
  .Register a Preprocessor
  ----
  JavaExtensionRegistry extensionRegistry = this.asciidoctor.javaExtensionRegistry(); // <1>
  
  extensionRegistry.preprocessor(ChangeAttributeValuePreprocessor.class); // <2>
  
  String content = asciidoctor.convertFile(new File(
                  "target/test-classes/changeattribute.adoc"),
                  new Options()); // <3>
  ----
  <1> `JavaExtensionRegistry` class is created.
  <2> `Preprocessor` extension is registered by using `Class` approach.
  <3> We can call any `convert` method as usual; no extra parameters are required.
  
  === Treeprocessor
  
  This extension detects literal blocks that contain terminal commands.
  It strips the prompt character and styles the command.
  
  [source]
  .Treeprocessor example
  ----
  public class TerminalCommandTreeprocessor extends Treeprocessor { // <1>
  
    private Document document;
  
    public TerminalCommandTreeprocessor(Map<String, Object> config) {
        super(config);
    }
  
    @Override
    public Document process(Document document) {
  
      this.document = document;
  
      final List<AbstractBlock> blocks = this.document.blocks(); // <2>
  
      for (int i = 0; i < blocks.size(); i++) {
          final AbstractBlock currentBlock = blocks.get(i);
          if(currentBlock instanceof Block) {
              Block block = (Block)currentBlock;
              List<String> lines = block.lines(); // <3>
              if (lines.size() > 0 && lines.get(0).startsWith("$")) {
                  blocks.set(i, convertToTerminalListing(block));
              }
          }
      }
  
      return this.document;
    }
  
    public Block convertToTerminalListing(Block block) {
  
      Map<String, Object> attributes = block.attributes();
      attributes.put("role", "terminal");
      StringBuilder resultLines = new StringBuilder();
  
      List<String> lines = block.lines();
  
      for (String line : lines) {
          if (line.startsWith("$")) {
              resultLines.append("<span class=\"command\">")
                         .append(line.substring(2, line.length()))
                         .append("</command");
          }
          else {
              resultLines.append(line);
          }
      }
  
      return createBlock(this.document, "listing", Arrays.asList(resultLines.toString()), attributes,
                new HashMap<Object, Object>()); // <4>
    }
  
  }
  ----
  <1> Class must extend from `Treeprocessor`.
  <2> The `document` instance can be used to retrieve all blocks of the current document.
  <3> All of the selcect block's lines are retrieved.
  <4> To create a new block, you must use the `createBlock` method. Set the parent document, the context (_listing_), the text content, attributes and options.
  
  [source]
  .Register a Treeprocessor
  ----
  JavaExtensionRegistry extensionRegistry = this.asciidoctor.javaExtensionRegistry(); // <1>
  
  extensionRegistry.treeprocessor("org.asciidoctor.extension.TerminalCommandTreeprocessor"); // <2>
  
  String content = asciidoctor.convertFile(new File(
                  "target/test-classes/sample-with-terminal-command.adoc"),
                  new Options()); // <3>
  ----
  <1> `JavaExtensionRegistry` class is created.
  <2> `Treeprocessor` extension is registered using fully qualified class name as `String`.
  <3> We can call any `convert` method as usually, no extra parameters are required.
  
  === Postprocessor
  
  This extension inserts custom footer text.
  
  [source]
  .Postprocessor example
  ----
  public class CustomFooterPostProcessor extends Postprocessor { // <1>
  
    public CustomFooterPostProcessor(Map<String, Object> config) {
        super(config);
    }
  
    @Override
    public String process(Document document, String output) { // <2>
  
        String copyright  = "Copyright Acme, Inc.";
  
        if(document.basebackend("html")) {
            org.jsoup.nodes.Document doc = Jsoup.parse(output, "UTF-8");
  
            Element contentElement = doc.getElementById("footer-text");
            contentElement.append(copyright);
  
            output = doc.html();
  
        }
        return output; // <3>
    }
  
  }
  ----
  <1> Class must extend from `Postprocessor`.
  <2> `process` method receives the `Document` instance and the document converted as `String`.
  <3> The content that will be written in document is returned.
  
  [source]
  .Register a Postprocessor
  ----
  JavaExtensionRegistry extensionRegistry = this.asciidoctor.javaExtensionRegistry(); // <1>
  
  extensionRegistry.postprocessor(new CustomFooterPostProcessor(
  				                        new HashMap<String, Object>())); // <2>
  
  String content = asciidoctor.convertFile(new File(
                "sample.adoc"),
                options); // <3>
  ----
  <1> `JavaExtensionRegistry` class is created.
  <2> `Postprocessor` extension is registered using an already created instance approach.
  <3> We can call any `convert` method as usually, no extra parameters are required.
  
  === Block processor
  
  This extension registers a custom block style named _yell_ that uppercases all the words.
  
  [source]
  .Block processor example
  ----
  public class YellBlock extends BlockProcessor { // <1>
  
    public YellBlock(String name, Map<String, Object> config) { // <2>
        super(name, config);
    }
  
    @Override
    public Object process(AbstractBlock parent, Reader reader, Map<String, Object> attributes) { // <3>
        List<String> lines = reader.readLines();
        String upperLines = null;
        for (String line : lines) {
            if (upperLines == null) {
                upperLines = line.toUpperCase();
            }
            else {
                upperLines = upperLines + "\n" + line.toUpperCase();
            }
        }
  
  		  return createBlock(parent, "paragraph", Arrays.asList(upperLines), attributes, new HashMap<Object, Object>()); // <4>
    }
  
  }
  ----
  <1> Class must extend from `BlockProcessor`.
  <2> Constructor must receive the name of the block and a `Map` for sending options to block.
  <3> `process` method receives the parent block, a reader, and attributes defined in block.
  <4> To create a new block we must use `createBlock` method. We must set the parent document, the context (listing), the text content, attributes and options.
  
  [source]
  .Register a Block processor
  ----
  JavaExtensionRegistry extensionRegistry = this.asciidoctor.javaExtensionRegistry(); // <1>
  
  extensionRegistry.block("yell", YellBlock.class); // <2>
  
  String content = asciidoctor.convertFile(new File(
                  "target/test-classes/sample-with-yell-block.adoc"),
                  new Options()); // <3>
  ----
  <1> `JavaExtensionRegistry` class is created.
  <2> `BlockProcessor` extension is registered with the context of block.
  <3> We can call any `convert` method as usually, no extra parameters are required.
  
  [source,asciidoc]
  .Example of Block processor
  ----
  [yell] <1>
  The time is now. Get a move on.
  ----
  <1> Note that _yell_ is the context where block lives and is the same as the first parameter of `block` method of `ExtensionRegistry` class.
  
  === Block macro processor
  
  This extension creates a block macro named gist for embedding a gist.
  
  [source]
  .Block macro processor example
  ----
  public class GistMacro extends BlockMacroProcessor { // <1>
  
    public GistMacro(String macroName, Map<String, Object> config) { // <2>
        super(macroName, config);
    }
  
    @Override
    public Block process(AbstractBlock parent, String target,
                          Map<String, Object> attributes) { // <3>
  
      String content = "<div class=\"content\">\n" +
         		"<script src=\"https://gist.github.com/"+target+".js\"></script>\n" +
         		"</div>";
  
      return createBlock(parent, "pass", Arrays.asList(content), attributes,
                          this.getConfig()); //<4>
    }
  
  }
  ----
  <1> Class must extend from `BlockMacroProcessor`.
  <2> Constructor must receive the macro name, and a `Map` for sending options to block..
  <3> `process` method receives the parent document, the content of the macro, and attributes defined in macro.
  <4> To create a new block we must use `createBlock` method. We must set the parent document, the context (listing), the text content, attributes and options.
  
  [source]
  .Register a Block macro processor
  ----
  JavaExtensionRegistry extensionRegistry = this.asciidoctor.javaExtensionRegistry(); // <1>
  
  extensionRegistry.blockMacro("gist", GistMacro.class); // <2>
  
  String content = asciidoctor.convertFile(new File(
                  "target/test-classes/sample-with-gist-macro.adoc"),
                  new Options()); // <3>
  ----
  <1> `JavaExtensionRegistry` class is created.
  <2> `BlockMacroProcessor` extension is registered with the name of the macro.
  <3> We can call any `convert` method as usually, no extra parameters are required.
  
  [source, asciidoc]
  .Example of Block macro processor
  ----
  .My Gist
  gist::123456[] // <1>
  ----
  <1> Note that _gist_ is the name of the macro and is the same as the first parameter of `blockMacro` method of `ExtensionRegistry` class.
  
  === Inline macro processor
  
  This extension creates an inline macro named man that links to a manpage.
  
  [source]
  .Inline macro processor example
  ----
  public class ManpageMacro extends InlineMacroProcessor { //<1>
  
    public ManpageMacro(String macroName, Map<String, Object> config) { // <2>
        super(macroName, config);
    }
  
    @Override
    protected String process(AbstractBlock parent, String target, Map<String, Object> attributes) { // <3>
  
        Map<String, Object> options = new HashMap<String, Object>();
        options.put("type", ":link");
        options.put("target", target + ".html");
        return createInline(parent, "anchor", target, attributes, options).convert(); // <4>
    }
  
  }
  ----
  <1> Class must extend from `InlineMacroProcessor`.
  <2> Constructor must receive the macro name, and a `Map` for passing options to extension.
  <3> `process` method receives the parent document, the content of the macro, and attributes defined in macro.
  <4> Because it is an inline macro, only a replacement string must be returned.
  
  [source]
  .Register an Inline macro processor
  ----
  JavaExtensionRegistry extensionRegistry = this.asciidoctor.javaExtensionRegistry(); // <1>
  
  extensionRegistry.inlineMacro("man", ManpageMacro.class); // <2>
  
  String content = asciidoctor.convertFile(new File(
                  "target/test-classes/sample-with-man-link.adoc"),
                  new Options()); // <3>
  ----
  <1> `ExtensionRegistry` class is created.
  <2> `InlineMacroProcessor` extension is registered with the name of the macro.
  <3> We can call any `convert` method as usually, no extra parameters are required.
  
  [source,asciidoc]
  .Example of Inline macro processor
  ----
  See man:gittutorial[7] to get started. <1>
  ----
  <1> Note that _man_ is the name of the macro and is the same as the first parameter of `inlineMacro` method of `ExtensionRegistry` class.
  
  === Include processor
  
  Include a file from a URI.
  
  [source]
  .Include processor example
  ----
  public class UriIncludeProcessor extends IncludeProcessor { // <1>
  
    public UriIncludeProcessor(Map<String, Object> config) {
      super(config);
    }
  
    @Override
    public boolean handles(String target) {
      return target.startsWith("http://") || target.startsWith("https://"); // <2>
    }
  
    @Override
    public void process(DocumentRuby document, PreprocessorReader reader, String target,
              Map<String, Object> attributes) {
  
      StringBuilder content = readContent(target);
      reader.push_include(content.toString(), target, target, 1, attributes); // <3>
  
    }
  
    private StringBuilder readContent(String target) {
  
      StringBuilder content = new StringBuilder();
  
      try {
  
        URL url = new URL(target);
        InputStream openStream = url.openStream();
  
        BufferedReader bufferedReader = new BufferedReader(
                      new InputStreamReader(openStream));
  
        String line = null;
        while ((line = bufferedReader.readLine()) != null) {
          content.append(line);
        }
  
        bufferedReader.close();
  
        } catch (MalformedURLException e) {
            throw new IllegalArgumentException(e);
        } catch (IOException e) {
            throw new IllegalArgumentException(e);
        }
        return content;
    }
  
  }
  ----
  <1> Class must extend from `IncludeProcessor`.
  <2> `handles` method is used by processor to decide if included element should be converted by this processor or not. `target` attribute is the value of `include` macro.
  <3> `push_include` method inserts new content (retrieved from the url) in current position of document.
  
  [source]
  .Register an Inline macro processor
  ----
  JavaExtensionRegistry extensionRegistry = this.asciidoctor.javaExtensionRegistry(); // <1>
  
  extensionRegistry.includeProcessor(UriIncludeProcessor.class); // <2>
  
  String content = asciidoctor.convertFile(new File(
                  "target/test-classes/sample-with-uri-include.adoc"),
                  new Options()); // <3>
  ----
  <1> `ExtensionRegistry` class is created.
  <2> `IncludeProcessor` extension is registered.
  <3> We can call any `convert` method as usually; no extra parameters are required.
  
  [source,asciidoc]
  .Example of include processor
  ....
  = Example of URI
  
  .Gemfile
  [source,ruby]
  ----
  \include::https://raw.githubusercontent.com/asciidoctor/asciidoctor/master/Gemfile[]
  ----
  ....
  
  === Unregistering extensions
  
  You can unregister all extensions by calling the `org.asciidoctor.Asciidoctor.unregisterAllExtensions()` method.
  
  === Ruby extensions
  
  You can even register extensions written in Ruby using AsciidoctorJ.
  To register a _Ruby_ extension you must get a `RubyExtensionRegistry` class instead of `JavaExtensionRegistry`.
  
  [source]
  .Register a Ruby extension in Java
  ----
  RubyExtensionRegistry rubyExtensionRegistry = this.asciidoctor.rubyExtensionRegistry(); // <1>
  rubyExtensionRegistry.loadClass(Class.class.getResourceAsStream("/YellRubyBlock.rb")).block("rubyyell", "YellRubyBlock"); // <2>
  
  String content = asciidoctor.convertFile(new File(
                  "target/test-classes/sample-with-ruby-yell-block.ad"),
                  options().toFile(false).get());
  ----
  <1> `rubyExtensionRegistry` method is called to get a rubyExtensionRegistry instance.
  <2> Ruby file containing a class implementing a Block extension is loaded inside the Ruby runtime. Then the block is registered with a name (rubyyell), and we pass the name of the class to be instantiated.
  
  [source,ruby]
  .YellBlock.rb
  ----
  require 'asciidoctor'
  require 'asciidoctor/extensions'
  
  class YellRubyBlock < Asciidoctor::Extensions::BlockProcessor
    option :contexts, [:paragraph]
    option :content_model, :simple
  
    def process parent, reader, attributes
      lines = reader.lines.map {|line| line.upcase.gsub(/\.( |$)/, '!\\1') }
      Asciidoctor::Block.new parent, :paragraph, :source => lines, :attributes => attributes
    end
  end
  ----
  
  === Extension SPI
  
  In previous examples, the extensions were registered manually.
  However, AsciidoctorJ provides another way to register extensions.
  If any implementation of the SPI interface is present on the classpath, it will be executed.
  
  To create an autoloadable extension you should do next steps.
  
  Create a class that implements `org.asciidoctor.extension.spi.ExtensionRegistry`.
  
  [source]
  .org.asciidoctor.extension.ArrowsAndBoxesExtension.java
  ----
  public class ArrowsAndBoxesExtension implements ExtensionRegistry { // <1>
  
    @Override
    public void register(Asciidoctor asciidoctor) { // <2>
  
      JavaExtensionRegistry javaExtensionRegistry = asciidoctor.javaExtensionRegistry();
      javaExtensionRegistry.postprocessor(ArrowsAndBoxesIncludesPostProcessor.class); // <3>
      javaExtensionRegistry.block("arrowsAndBoxes", ArrowsAndBoxesBlock.class);
  
    }
  
  }
  ----
  <1> To autoload extensions you need to implement `ExtensionRegistry`.
  <2> AsciidoctorJ will automatically run the `register` method. The method is responsible for registering all extensions.
  <3> All required Java extensions are registered.
  
  Next, you need to create a file called `org.asciidoctor.extension.spi.ExtensionRegistry` inside `META-INF/services` with the implementation's full qualified name.
  
  [source]
  .META-INF/services/org.asciidoctor.extension.spi.ExtensionRegistry
  ----
  org.asciidoctor.extension.ArrowsAndBoxesExtension
  ----
  
  And that's all.
  Now when a `.jar` file containing the previous structure is dropped inside classpath of AsciidoctorJ, the `register` method will be executed automatically and the extensions will be registered.
  
  == Converting to EPUB3
  
  The Asciidoctor EPUB3 gem (asciidoctor-epub3) is bundled inside the AsciidoctorJ EPUB3 jar (asciidoctorj-epub3).
  To use it, simply add the asciidoctorj-epub3 jar to your dependencies.
  The version of the AsciidoctorJ EPUB3 jar aligns with the version of the Asciidoctor EPUB3 gem.
  
  Here's how you can add the AsciidoctorJ EPUB3 jar to your Maven dependencies:
  
  [source,xml]
  ----
  <dependencies>
    <dependency>
      <groupId>org.asciidoctor</groupId>
      <artifactId>asciidoctorj-epub3</artifactId>
      <version>1.5.0-alpha.4</version>
      <scope>runtime</scope>
    </dependency>
  </dependencies>
  ----
  
  Once you've added the AsciidoctorJ EPUB3 jar to your classpath, you can set the `backend` attribute to `epub3`.
  The document will be converted to the http://idpf.org/epub/30[EPUB3^] format.
  
  CAUTION: The [app]_asciidoctor-epub3_ gem is alpha.
  While it can be used successfully, there may be bugs and its functionality may change in incompatible ways before the first stable release.
  In other words, by using it, you are also testing it ;)
  
  Let's see an example of how to use AsciidoctorJ with the EPUB3 converter.
  
  [source,asciidoc]
  .spine.adoc
  ----
  = Book Title
  Author Name
  :imagesdir: images <1>
  
  include::content-document.adoc[] <2>
  ----
  <1> The EPUB3 converter requires the value of the `imagesdir` attribute to be `images`.
  <2> The EPUB3 converter must be run on a _spine_ document that has at least one include directive (and no other body content) in order to function properly.
  
  [source,asciidoc]
  .content-document.adoc
  ----
  = Content Title
  Author Name
  
  [abstract]
  This is the actual content.
  
  == First Section
  
  And off we go.
  ----
  
  And finally we can convert the document to EPUB3 using AsciidoctorJ.
  
  [source]
  ----
  asciidoctor.convertFile(new File("spine.adoc"),
                  options().safe(SafeMode.SAFE).backend("epub3").get()); // <1> <2>
  
  assertThat(new File("target/test-classes/index.epub").exists(), is(true));
  ----
  <1> Currently, the EPUB3 converter must be run in `SAFE` or `UNSAFE` mode due to a bug
  <2> `epub3` is the name of the backend that must be set to convert to EPUB3.
  
  // TODO document how to convert to KF8/MOBI (need to set KINDLEGEN environment variable)
  // TODO add section that covers using Asciidoctor PDF via the AsciidoctorJ PDF jar
  
  == Loading Ruby libraries
  
  Simple extensions may be fully implemented in Java, but if you want to create complex extensions you can mix Ruby and Java code.
  This means that you may need to execute a Ruby file or a RubyGem (i.e., gem) inside your extension.
  
  To load a Ruby file inside the Ruby runtime, you can use `org.asciidoctor.internal.RubyUtils.loadRubyClass(Ruby, InputStream)`.
  You can also load a gem using an API that wraps Ruby's `require` command.
  The gem must be available inside the classpath.
  Next run `org.asciidoctor.internal.RubyUtils.requireLibrary(Ruby, String)`, passing the name of the gem as the second argument.
  
  // SW or AS: show an example of loading Ruby libraries or point to an external resource showing examples/further documentation.
  
  == JRuby instance
  
  Sometimes you may need the Ruby runtime used inside AsciidoctorJ.
  One reason is because you are using JRuby outside AsciidoctorJ and you want to reuse the same instance.
  Another reason is that you need to instantiate by yourself an Asciidoctor Ruby object.
  
  To get this instance you can use `org.asciidoctor.internal.JRubyRuntimeContext.get()` to get it.
  
  == GEM_PATH
  
  By default, AsciidoctorJ comes with all required gems bundled within the jar.
  But in some circumstances like OSGi environments you may require to store gems in an external folder and be loaded by AsciidoctorJ.
  To accomplish this scenario, `create` method provides a parameter to set folder where gems are present.
  Internally, AsciidoctorJ will set `GEM_PATH` environment variable to given path.
  
  [source]
  .Example of setting GEM_PATH
  ----
  import static org.asciidoctor.Asciidoctor.Factory.create;
  import org.asciidoctor.Asciidoctor;
  
  Asciidoctor asciidoctor = create("/my/gem/path"); // <1>
  ----
  <1> Creates an `Asciidoctor` instance with given `GEM_PATH` location.
  
  == Optimization
  
  JRuby may start slower than expected versus the C-based Ruby implementation (MRI).
  Fortunately, JRuby offers flags that can improve the start time and tune applications.
  Several Java flags can also be used in conjunction with or apart from the JRuby flags in order to improve the start time even more.
  
  // SW: Need examples of JRuby and Java flags being used
  
  For small tasks such as converting an AsciiDoc document, two JRuby flags can drastically improve the start time:
  
  [cols="1m,2", width="50%"]
  .JRuby flags
  |===
  |Name |Value
  
  |jruby.compat.version
  |RUBY1_9
  