blob: 8fce1d0d4673d31275af5b51ebe9b1c9243d60c7 [file] [log] [blame]
<?php
/**
* File containing the ezcDocumentWikiDocbookVisitor class
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
* @package Document
* @version //autogen//
* @copyright Copyright (C) 2005-2010 eZ Systems AS. All rights reserved.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
*/
/**
* Docbook visitor for the Wiki AST.
*
* @package Document
* @version //autogen//
*/
class ezcDocumentWikiDocbookVisitor extends ezcDocumentWikiVisitor
{
/**
* Mapping of class names to internal visitors for the respective nodes.
*
* @var array
*/
protected $complexVisitMapping = array(
'ezcDocumentWikiTextNode' => 'visitText',
'ezcDocumentWikiSeparatorNode' => 'visitText',
'ezcDocumentWikiBoldNode' => 'visitEmphasisMarkup',
'ezcDocumentWikiItalicNode' => 'visitEmphasisMarkup',
'ezcDocumentWikiUnderlineNode' => 'visitEmphasisMarkup',
'ezcDocumentWikiTitleNode' => 'visitTitle',
'ezcDocumentWikiLinkNode' => 'visitLink',
'ezcDocumentWikiExternalLinkNode' => 'visitExternalLink',
'ezcDocumentWikiInternalLinkNode' => 'visitExternalLink',
'ezcDocumentWikiInterWikiLinkNode' => 'visitExternalLink',
'ezcDocumentWikiBulletListNode' => 'visitList',
'ezcDocumentWikiEnumeratedListNode' => 'visitList',
'ezcDocumentWikiImageNode' => 'visitImages',
'ezcDocumentWikiLiteralBlockNode' => 'visitLiteralBlock',
'ezcDocumentWikiInlineLiteralNode' => 'visitLiteral',
'ezcDocumentWikiTableRowNode' => 'visitTableRow',
'ezcDocumentWikiTableCellNode' => 'visitTableCell',
'ezcDocumentWikiLineBreakNode' => 'visitLineBreak',
'ezcDocumentWikiParagraphNode' => 'visitParagraph',
'ezcDocumentWikiBlockquoteNode' => 'visitBlockquote',
'ezcDocumentWikiFootnoteNode' => 'visitFootnote',
'ezcDocumentWikiPluginNode' => 'visitPlugin',
// Node markup is ignored, because there is no equivalent in docbook
'ezcDocumentWikiDeletedNode' => 'visitChildren',
);
/**
* Direct mapping of AST node class names to docbook element names.
*
* @var array
*/
protected $simpleVisitMapping = array(
'ezcDocumentWikiSectionNode' => 'section',
'ezcDocumentWikiInlineQuoteNode' => 'quote',
'ezcDocumentWikiSuperscriptNode' => 'superscript',
'ezcDocumentWikiSubscriptNode' => 'subscript',
'ezcDocumentWikiMonospaceNode' => 'literal',
'ezcDocumentWikiBulletListItemNode' => 'listitem',
'ezcDocumentWikiEnumeratedListItemNode' => 'listitem',
'ezcDocumentWikiPageBreakNode' => 'beginpage',
'ezcDocumentWikiTableNode' => 'table',
);
/**
* Array with nodes, which can be ignored during the transformation
* process, they only provide additional information during preprocessing.
*
* @var array
*/
protected $skipNodes = array();
/**
* DOM document
*
* @var DOMDocument
*/
protected $document;
/**
* Docarate Wiki AST
*
* Visit the Wiki abstract syntax tree.
*
* @param ezcDocumentWikiDocumentNode $ast
* @return mixed
*/
public function visit( ezcDocumentWikiDocumentNode $ast )
{
parent::visit( $ast );
// Create article from AST
$imp = new DOMImplementation();
$dtd = $imp->createDocumentType( 'article', '-//OASIS//DTD DocBook XML V4.5//EN', 'http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd' );
$this->document = $imp->createDocument( 'http://docbook.org/ns/docbook', '', $dtd );
$this->document->formatOutput = true;
// $root = $this->document->createElement( 'article' );
$root = $this->document->createElementNs( 'http://docbook.org/ns/docbook', 'article' );
$this->document->appendChild( $root );
// Visit all childs of the AST root node.
foreach ( $ast->nodes as $node )
{
$this->visitNode( $root, $node );
}
return $this->document;
}
/**
* Visit single AST node
*
* Visit a single AST node, may be called for each node found anywhere
* as child. The current position in the DOMDocument is passed by a
* reference to the current DOMNode, which is operated on.
*
* @param DOMNode $root
* @param ezcDocumentWikiNode $node
* @return void
*/
protected function visitNode( DOMNode $root, ezcDocumentWikiNode $node )
{
// Iterate over available visitors and use them to visit the nodes.
foreach ( $this->complexVisitMapping as $class => $method )
{
if ( $node instanceof $class )
{
return $this->$method( $root, $node );
}
}
// Check if we have a simple class to element name mapping
foreach ( $this->simpleVisitMapping as $class => $elementName )
{
if ( $node instanceof $class )
{
$element = $this->document->createElement( $elementName );
$root->appendChild( $element );
foreach ( $node->nodes as $child )
{
$this->visitNode( $element, $child );
}
return;
}
}
// Check if you should just ignore the node for rendering
foreach ( $this->skipNodes as $class )
{
if ( $node instanceof $class )
{
return;
}
}
// We could not find any valid visitor.
throw new ezcDocumentMissingVisitorException( get_class( $node ), $node->token->line, $node->token->position );
}
/**
* Visit emphasis markup
*
* @param DOMNode $root
* @param ezcDocumentWikiNode $node
* @return void
*/
protected function visitEmphasisMarkup( DOMNode $root, ezcDocumentWikiNode $node )
{
$markup = $this->document->createElement( 'emphasis' );
if ( $node instanceof ezcDocumentWikiBoldNode )
{
$markup->setAttribute( 'Role', 'strong' );
}
$root->appendChild( $markup );
foreach ( $node->nodes as $child )
{
$this->visitNode( $markup, $child );
}
}
/**
* Visit section titles
*
* @param DOMNode $root
* @param ezcDocumentWikiNode $node
* @return void
*/
protected function visitTitle( DOMNode $root, ezcDocumentWikiNode $node )
{
$title = $this->document->createElement( 'title' );
$root->appendChild( $title );
foreach ( $node->nodes as $child )
{
$this->visitNode( $title, $child );
}
}
/**
* Visit external link node
*
* @param DOMNode $root
* @param ezcDocumentWikiNode $node
* @return void
*/
protected function visitExternalLink( DOMNode $root, ezcDocumentWikiNode $node )
{
$link = $this->document->createElement( 'ulink' );
$link->setAttribute( 'url', $node->token->content );
$link->appendChild( new DOMText( $node->token->content ) );
$root->appendChild( $link );
}
/**
* Visit link node
*
* Docbook has no support for description of links, so that the description
* elements in the AST are omitted.
*
* @param DOMNode $root
* @param ezcDocumentWikiNode $node
* @return void
*/
protected function visitLink( DOMNode $root, ezcDocumentWikiNode $node )
{
$link = $this->document->createElement( 'ulink' );
$link->setAttribute( 'url', $linkUrl = $this->nodeListToString( $node->link ) );
$root->appendChild( $link );
if ( $node->nodes === array() )
{
$link->appendChild( new DOMText( $linkUrl ) );
}
else
{
foreach ( $node->nodes as $child )
{
$this->visitNode( $link, $child );
}
}
}
/**
* Visit list
*
* Ensure stacked lists are created inside another list item.
*
* @param DOMNode $root
* @param ezcDocumentWikiNode $node
* @return void
*/
protected function visitList( DOMNode $root, ezcDocumentWikiNode $node )
{
if ( ( $root->tagName === 'itemizedlist' ) ||
( $root->tagName === 'orderedlist' ) )
{
$listitem = $this->document->createElement( 'listitem' );
$root->appendChild( $listitem );
$root = $listitem;
}
$list = $this->document->createElement(
$node instanceof ezcDocumentWikiBulletListNode ? 'itemizedlist' : 'orderedlist'
);
$root->appendChild( $list );
foreach ( $node->nodes as $child )
{
$this->visitNode( $list, $child );
}
}
/**
* Is inline node?
*
* Check if contents of the current node are a inline node
*
* @param DOMNode $node
* @return bool
*/
protected function isInlineNode( DOMNode $node )
{
return in_array( $node->tagName, array(
'para',
'acronym',
'anchor',
'author',
'citation',
'email',
'emphasis',
'footnote',
'footnoteref',
'inlinemediaobject',
'literal',
'quote',
'subscript',
'superscript',
'link',
'ulink',
) );
}
/**
* Visit images
*
* @param DOMNode $root
* @param ezcDocumentWikiNode $node
* @return void
*/
protected function visitImages( DOMNode $root, ezcDocumentWikiNode $node )
{
$media = $this->document->createElement( $this->isInlineNode( $root ) ? 'inlinemediaobject' : 'mediaobject' );
$root->appendChild( $media );
$imageObject = $this->document->createElement( 'imageobject' );
$media->appendChild( $imageObject );
$image = $this->document->createElement( 'imagedata' );
$image->setAttribute( 'fileref', $this->nodeListToString( $node->resource ) );
$imageObject->appendChild( $image );
// Handle optional settings on images
if ( $node->title !== array() )
{
$text = $this->document->createElement( 'textobject' );
$media->appendChild( $text );
foreach ( $node->title as $child )
{
$this->visitNode( $text, $child );
}
}
if ( $node->width )
{
$image->setAttribute( 'width', (int) $node->width );
}
if ( $node->height )
{
$image->setAttribute( 'depth', (int) $node->height );
}
if ( $node->alignement )
{
$image->setAttribute( 'align', $node->alignement );
}
foreach ( $node->nodes as $child )
{
$this->visitNode( $list, $child );
}
}
/**
* Visit literal block
*
* @param DOMNode $root
* @param ezcDocumentWikiNode $node
* @return void
*/
protected function visitLiteralBlock( DOMNode $root, ezcDocumentWikiNode $node )
{
$literal = $this->document->createElement( 'literallayout', htmlspecialchars( $node->token->content ) );
$root->appendChild( $literal );
}
/**
* Visit literal
*
* @param DOMNode $root
* @param ezcDocumentWikiNode $node
* @return void
*/
protected function visitLiteral( DOMNode $root, ezcDocumentWikiNode $node )
{
$literal = $this->document->createElement( 'literal', htmlspecialchars( $node->token->content ) );
$root->appendChild( $literal );
}
/**
* Visit table row
*
* Visit a table row and decide if it belongs into a tbody or a thead
* section.
*
* @param DOMNode $root
* @param ezcDocumentWikiNode $node
* @return void
*/
protected function visitTableRow( DOMNode $root, ezcDocumentWikiNode $node )
{
$header = true;
foreach ( $node->nodes as $cell )
{
$header = $header && $cell->header;
}
// Get last child element in table
if ( !( $last = $root->lastChild ) ||
( $last->nodeType !== XML_ELEMENT_NODE ) )
{
$last = false;
}
$type = $header ? 'thead' : 'tbody';
if ( ( $last === false ) ||
( $last->tagName !== $type ) )
{
$wrapper = $this->document->createElement( $type );
$root->appendChild( $wrapper );
$root = $wrapper;
}
else
{
$root = $last;
}
$row = $this->document->createElement( 'row' );
$root->appendChild( $row );
foreach ( $node->nodes as $child )
{
$this->visitNode( $row, $child );
}
}
/**
* Visit table cell
*
* Visit a table cell and additionally always create an inner paragraph.
*
* @param DOMNode $root
* @param ezcDocumentWikiNode $node
* @return void
*/
protected function visitTableCell( DOMNode $root, ezcDocumentWikiNode $node )
{
$cell = $this->document->createElement( 'entry' );
$root->appendChild( $cell );
$this->visitParagraph( $cell, $node );
}
/**
* Visit line break
*
* @param DOMNode $root
* @param ezcDocumentWikiNode $node
* @return void
*/
protected function visitLineBreak( DOMNode $root, ezcDocumentWikiNode $node )
{
$root->appendChild( new DOMText( "\n" ) );
// Mark paragraph, so it will be converted into a literallayout
// element.
if ( $root->tagName === 'para' )
{
$root->setAttribute( 'type', 'literallayout' );
}
else
{
$this->triggerError(
E_NOTICE, 'Intentional line break outside of paragraph ignored.',
null, $node->token->line, $node->token->position
);
}
}
/**
* Visit paragraph
*
* @param DOMNode $root
* @param ezcDocumentWikiNode $node
* @return void
*/
protected function visitParagraph( DOMNode $root, ezcDocumentWikiNode $node )
{
$para = $this->document->createElement( 'para' );
$root->appendChild( $para );
foreach ( $node->nodes as $child )
{
$this->visitNode( $para, $child );
}
// Check if paragraph should be converted into a literalayout section,
// because it contains intentional line breaks. This marker is set by
// the visitLineBreak() method.
if ( $para->hasAttribute( 'type' ) &&
( $para->getAttribute( 'type' ) === 'literallayout' ) )
{
// Change paragraph into a literallayout section
$newPara = $this->document->createElement( 'literallayout' );
$newPara->setAttribute( 'class', 'normal' );
$root->appendChild( $newPara );
// Move all childs to new paragraph
foreach ( $para->childNodes as $child )
{
$newPara->appendChild( $child->cloneNode( true ) );
}
// Remove old paragraph
$root->removeChild( $para );
}
}
/**
* Visit blockquote
*
* @param DOMNode $root
* @param ezcDocumentWikiNode $node
* @return void
*/
protected function visitBlockquote( DOMNode $root, ezcDocumentWikiNode $node )
{
$blockquote = $this->document->createElement( 'blockquote' );
$root->appendChild( $blockquote );
$para = $this->document->createElement( 'para' );
$blockquote->appendChild( $para );
foreach ( $node->nodes as $child )
{
$this->visitNode( $para, $child );
}
}
/**
* Visit footnote
*
* @param DOMNode $root
* @param ezcDocumentWikiNode $node
* @return void
*/
protected function visitFootnote( DOMNode $root, ezcDocumentWikiNode $node )
{
$footnote = $this->document->createElement( 'footnote' );
$root->appendChild( $footnote );
$para = $this->document->createElement( 'para' );
$footnote->appendChild( $para );
foreach ( $node->nodes as $child )
{
$this->visitNode( $para, $child );
}
}
/**
* Visit plugin
*
* @param DOMNode $root
* @param ezcDocumentWikiNode $node
* @return void
*/
protected function visitPlugin( DOMNode $root, ezcDocumentWikiNode $node )
{
$handlerClass = $this->wiki->getPluginHandler( $node->type );
$pluginHandler = new $handlerClass( $this->ast, $this->path, $node );
$pluginHandler->toDocbook( $this->document, $root );
}
}
?>