Rendering Module

Version 53.1 by Vincent Massol on 2010/11/05 10:12

Rendering Module

This module is in charge of converting textual input in a given syntax into some rendered output. It's used in XWiki notably for rendering pages in XHTML, for performing syntax conversions, for performing page refactorings and to provide users with access to the page's structured information directly inside their wiki pages using one of the supported scripting macros.

Features

  • Parsers for multiple syntaxes
  • Round trip between XWiki Syntax 2.0 and XHTML. This features allows us to have a strong WYSIWYG editor that doesn't loose information when editing wiki pages. It also allows us to import Office documents into XWiki without loosing information.
  • Macro support.
  • Ability to get the result of the parsing as an AST tree (called XDOM) which can then be used to get access to all structured elements from the flat text input.
  • Supports wiki syntax in link labels even for syntaxes that don't support it.
  • Automatic conversion from any of the supported input syntaxes to XWiki Syntax 2.0 or to XHTML.

General Architecture

rendering.png

  • Parser: Parses some textual input in a given syntax and generate a XDOM object which is an AST representing the input into structured blocks.
  • Renderer: Takes a XDOM as input and generates some output.
  • Transformation: Takes some XDOM and modifies it to generate modified XDOM. One important Transformation registered by default is the MacroTransformation which looks for all Macro Blocks in the XDOM object and replaces them by blocks generated by the various Macros. Note that executing Transformations is an optional step and if you don't run them then you'll get a XDOM object without any transformation applied on it (e.g. without Macros executed).
  • Macro: Takes a Macro definition as input and generates XDOM Blocks.

Macro Execution Process

Macros are executed by the Macro Transformation. Here are rules that apply:

  • Macros can be registered for a given syntax or for all syntaxes.
  • Macros also have a priority which allows some macros to execute before or after other macros. For example the Include macro runs first and the Table Of Content macro executes last (so that it can take into account all headers created by other macros).
  • The Macro Transformation finds all Macro Blocks defined in the passed XDOM object. For each Macro Block found it verifies if there's a Macro registered for that Macro Block and for the target syntax. It then sorts all Macros by priority and execute them one by one, replacing the Macro Block with the list of Blocks generated by the Macro, till there are no Macro Blocks left in the XDOM. Note that Macro can generate Macro Blocks.

Supported Syntaxes

Input Syntaxes:

  • XWiki Syntax 1.0 (xwiki/1.0)
  • XWiki Syntax 2.0 (xwiki/2.0)
  • Confluence Syntax (confluence/1.0)
  • JSPWiki Syntax (jspwiki/1.0)
  • TWiki Syntax (twiki/1.0)
  • Media Wiki Syntax (mediawiki/1.0)
  • Creole 1.0 Syntax (creole/1.0)
  • XHTML and HTML Syntaxes (xhtml/1.0 and html/4.01)
  • Plain Text (plain/1.0)

Output Syntaxes:

  • XWiki Syntax 2.0 (xwiki/2.0)
  • XHTML (xhtml/1.0)
  • Plain Text (plain/1.0): Print all than can be rendered in a simple notepad-like editor such as words, special symbols and spaces. It also generates link labels for links that have no labels and print the generated labels. Last it provides very basic formatting (e.g. separates paragraphs with new lines and separates list items with new lines).

Quick Examples

These examples are for XWiki Enterprise 2.0M3 and beyond(you can download them here). For versions between 1.9 and 2.0M2, go here.

Render XWiki Syntax 2.0 content into XHTML

// Use a the Converter component to convert between one syntax to another.
Converter converter = componentManager.lookup(Converter.class);

// Convert input in XWiki Syntax 2.0 into XHTML. The result is stored in the printer.
WikiPrinter printer = new DefaultWikiPrinter();
converter.convert(new StringReader("This is **bold**"), Syntax.XWIKI_2_0, Syntax.XHTML_1_0, printer);

Assert.assertEquals("<p>This is <strong>bold</strong></p>", printer.toString());

Modify all Links to be displayed in italics

// Parse XWiki 2.0 Syntax using a Parser.
Parser parser = componentManager.lookup(Parser.class, Syntax.XWIKI_2_0.toIdString());
XDOM xdom = parser.parse(new StringReader("This a [[link>MyPage]]"));
       
// Find all links and make them italic
for (LinkBlock block : xdom.getChildrenByType(LinkBlock.class, true)) {
    Block parentBlock = block.getParent();
    Block newBlock = new FormatBlock(Collections.<Block>singletonList(block), Format.ITALIC);
    parentBlock.replaceChild(newBlock, block);
}

// Execute transformations (for example this executes the Macros which are implemented as Transformations).
TransformationManager txManager = componentManager.lookup(TransformationManager.class);
txManager.performTransformations(xdom, parser.getSyntax());

// Generate XWiki 2.0 Syntax as output for example
WikiPrinter printer = new DefaultWikiPrinter();
BlockRenderer renderer = componentManager.lookup(BlockRenderer.class, Syntax.XWIKI_2_0.toIdString());
renderer.render(xdom, printer);

Assert.assertEquals("This a //[[link>MyPage]]//", printer.toString());

Tutorials

Adding a new Syntax

Adding support for a new syntax (i.e. the ability to write page contents using a new syntax) is as easy as implementing a Parser. To do so simply implement the Parser interface and register it as a component against the Component Manager.

Example:

public class MyParser implements Parser
{
   private static final Syntax SYNTAX = new Syntax(new SyntaxType("mysyntax", "My Syntax"), "1.0");

   public Syntax getSyntax()
   {
       return SYNTAX;
   }

   public XDOM parse(Reader source) throws ParseException
   {
        XDOM xdom = new XDOM(Collections.singletonList(new WordBlock("amazing")));
       return xdom;
   }
}

Adding a new Macro

Beginning with XWiki Enterprise 2.0M2 macros can be written in two ways:

Adding a new Renderer

Update 2009-08-08: Starting with XWiki Enterprise 2.0M3 Renderers are now components and a new tutorial needs to be written.

You have to implement the Renderer interface.
Examples of Renderer source code:

Adding a new Transformation

Starting with XWiki version 2.6, Transformations are not executed in the WYSIWYG editor.  The reason is that we don't yet have a generic marker mechanism in place so that the WYSIWYG editor considers transformed content as read only. If you're interested in this topic follow this JIRA issue.

To create a Transformation, create a Component implementing the org.xwiki.rendering.transformation.Transformation interface. To make it even easier you can simply extend org.xwiki.rendering.transformation.AbstractTransformation.

To activate your transformation, edit the xwiki.properties file and look for the rendering.transformations property.

Example:
@Component("wikiword")
public class WikiWordTransformation extends AbstractTransformation
{
   /**
     * Regex Pattern to recognize a WikiWord.
     */

   private static final Pattern WIKIWORD_PATTERN = Pattern.compile(
       "\\p{javaUpperCase}+\\p{javaLowerCase}+(\\p{javaUpperCase}\\p{javaLowerCase}*)+");

   /**
     * {@inheritDoc}
     * @see AbstractTransformation#transform(Block, TransformationContext)
     */

   public void transform(Block block, TransformationContext transformationContext) throws TransformationException
   {
       // Find all Word blocks and for each of them check if they're a wiki word or not
       for (WordBlock wordBlock : block.getChildrenByType(WordBlock.class, true)) {
            Matcher matcher = WIKIWORD_PATTERN.matcher(wordBlock.getWord());
           if (matcher.matches()) {
                ResourceReference linkReference = new DocumentResourceReference(wordBlock.getWord());
                wordBlock.getParent().replaceChild(new LinkBlock(wordBlock.getChildren(), linkReference, false),
                    wordBlock);
           }
       }
   }
}

Check out the full code with tests

Adding a new Link Type

This domain of the XWiki Rendering is not stabilized yet and is subject to changes in future releases: APIs might evolve a bit and packages could change

Starting with XE 2.5, when using the XWiki Syntax 2.1, it's possible to extend the link syntax. The generic link syntax is: label and it's possible to add new referenceType. This is a several step process:

  • Implement a component with the org.xwiki.rendering.parser.ResourceReferenceTypeParser role.
  • Implement a component with the org.xwiki.rendering.internal.renderer.xhtml.link.XHTMLLinkTypeRenderer role.

Understanding the directory structure

If you check out the Rendering Module from SVN you'll get the following directory structure where each directory is a Maven project and generates a JAR:

  • xwiki-rendering-api: The Rendering API and default implementations that work inside and outside of XWiki.
  • xwiki-rendering-macros: Each submodule contains a macro, delivered as a JAR.
  • xwiki-rendering-syntaxes: Various Parser and Renderer implementations for various syntaxes.
    • xwiki-rendering-syntax-doxia: An experimental Doxia bridge and the TWiki parser (using the Doxia TWiki parser implementation under the hood).
    • xwiki-rendering-syntax-wikimodel: A WikiModel bridge and the XWiki 2.0, Creole, Confluence, XHTML, JSPWiki and MediaWiki parsers (using the WikiModel parser implementations under the hood - to which the XWiki project has largely contributed to).
    • xwiki-rendering-syntax-xwiki10: A XWiki Syntax 1.0 parser. It's used for example to convert wiki pages from XWiki Syntax 1.0 to XWiki Syntax 2.0 or to be able to include 1.0 syntax page into 2.0 syntax page.
  • xwiki-rendering-standalone: Generates an uberjar to make it easy to use the XWiki Rendering outside of XWiki (when not using Maven).
  • xwiki-rendering-tests: Integration tests suite for the Rendering Module.
  • xwiki-rendering-xwiki: XWiki-specific implementation for some interfaces defined in xwiki-rendering-api. This module JAR shouldn't be used when using the XWiki Rendering outside of XWiki since it draws lots of transitive dependencies from other XWiki module and contains XWiki-specific notions.

Using the Rendering module outside of XWiki

It's possible to use the Rendering Module for your own projects as a library.

Start by understanding the module's directory structure.

Then you need to get hold of the Rendering Module JARs to put in your runtime classpath. More specifically you'll need:

In order to get these JAR you can simply reference them as dependencies from your own project if you're using Maven 2. For example (note that haven't added a dependency on xwiki-rendering-api since the xwiki-core-rendering-parser-wikimodel one gets it transitively):

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>com.acme</groupId>
 <artifactId>acme</artifactId>
 <name>Acme</name>
 <version>1.0-SNAPSHOT</version>
 <packaging>jar</packaging>
 <dependencies>
   <dependency>
     <groupId>org.xwiki.platform</groupId>
     <artifactId>xwiki-core-rendering-syntax-wikimodel</artifactId>
     <version>2.2.4</version>
   </dependency>
   <dependency>
     <groupId>org.xwiki.platform</groupId>
     <artifactId>xwiki-core-component-default</artifactId>
     <version>2.2.4</version>
   </dependency>
 </dependencies>
</project>

Note that you can get the full transitive dependency list by typing mvn dependency:list or mvn dependency:tree and you can get an uberjar with mvn assembly:single -DdescriptorId=jar-with-dependencies.

If you want to use macros you'll also need to add dependencies for them. For example if you want to use the code and message (info, error, warning) macros you'd write:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>com.acme</groupId>
 <artifactId>acme</artifactId>
 <name>Acme</name>
 <version>1.0-SNAPSHOT</version>
 <packaging>jar</packaging>
 <dependencies>
   <dependency>
     <groupId>org.xwiki.platform</groupId>
     <artifactId>xwiki-core-rendering-syntax-wikimodel</artifactId>
     <version>2.2.4</version>
   </dependency>
   <dependency>
     <groupId>org.xwiki.platform</groupId>
     <artifactId>xwiki-core-component-default</artifactId>
     <version>2.2.4</version>
   </dependency>
   <dependency>
     <groupId>org.xwiki.platform</groupId>
     <artifactId>xwiki-core-rendering-macro-message</artifactId>
     <version>2.2.4</version>
   </dependency>
   <dependency>
     <groupId>org.xwiki.platform</groupId>
     <artifactId>xwiki-core-rendering-macro-code</artifactId>
     <version>2.2.4</version>
   </dependency>
 </dependencies>
</project>

If you're not using Maven then we've made available a Standalone JAR which contains all the classes in one JAR (it doesn't contain macros though, you'll need to add them separately for now). However note that right now this standalone JAR is using Class relocation to try to prevent JAR version hell but under some special conditions it could fail (like running it in Google App Engine).

Once you have the correct classpath, all you need to be able to use the Rendering Module is to create an instance of the XWiki Embeddable Component Manager as follows:

// Initialize Rendering components and allow getting instances
EmbeddableComponentManager ecm = new EmbeddableComponentManager();
ecm.initialize(this.getClass().getClassLoader());

Then you can use any of the examples defined above.

Note that the Rendering Module code supports 2 cases: if you're inside a wiki or not. If you're not inside a wiki for example the Link and Image Renderers will only handle links and images pointing to URLs and not handle them if they point to a document. To decide if it's inside a wiki or not, the Rendering Module code checks to see if it can find a component implementing the WikiModel interface (not to be confused with the WikiModel project we use too). This interfaces exposes methods corresponding to features that any wiki should have. This allows you to integrate the XWiki Rendering Module with your own wiki.

Get Connected