package org.apache.maven.doxia.sink.impl;

/*
 * 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.
 */

import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.Writer;

import org.apache.maven.doxia.AbstractModuleTest;
import org.apache.maven.doxia.logging.PlexusLoggerWrapper;
import org.apache.maven.doxia.sink.Sink;
import org.apache.maven.doxia.sink.SinkEventAttributes;
import org.codehaus.plexus.DefaultPlexusContainer;
import org.codehaus.plexus.util.IOUtil;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.xmlunit.matchers.CompareMatcher.isIdenticalTo;

/**
 * Abstract base class to test sinks.
 */
public abstract class AbstractSinkTest
    extends AbstractModuleTest
{
    private final CharArrayWriter testWriter = new CharArrayWriter();
    private Sink sink;

    /**
     * Resets the writer and creates a new sink with it.
     *
     * {@inheritDoc}
     *
     * @throws java.lang.Exception if something goes wrong.
     */
    @Override
    protected void setUp()
        throws Exception
    {
        super.setUp();

        testWriter.reset();
        sink = createSink( testWriter );
        sink.enableLogging( new PlexusLoggerWrapper( ( ( DefaultPlexusContainer )getContainer() ).getLogger() ) );
    }

    /**
     * Ability to wrap the xmlFragment with a roottag and namespaces, when required
     *
     * @param xmlFragment XML fragment to wrap
     * @return valid XML
     */
    protected String wrapXml( String xmlFragment )
    {
        return xmlFragment;
    }

    /**
     * Transforms a given string to be compatible to XML comments.
     *
     * @param comment The string to transform.
     *
     * @return The given string transformed to be compatible to XML comments.
     *
     * @see <a href="http://www.w3.org/TR/2000/REC-xml-20001006#sec-comments">
     *   http://www.w3.org/TR/2000/REC-xml-20001006#sec-comments</a>
     * @since 1.7
     */
    protected static String toXmlComment( final String comment )
    {
        String processed = comment;

        if ( processed != null )
        {
            while ( processed.contains( "--" ) )
            {
                processed = processed.replace( "--", "- -" );
            }

            if ( processed.endsWith( "-" ) )
            {
                processed += " ";
            }
        }

        return processed;
    }

    // ---------------------------------------------------------------------
    // Common test cases
    // ----------------------------------------------------------------------

    /**
     * Tests that the current sink is able to render the common test document. If the sink is an Xml sink defined
     * by {@link #isXmlSink()}, it uses an Xml Writer defined by {@link #getXmlTestWriter(String)}. If not, it uses
     * the Writer defined by {@link #getTestWriter(String)}.
     *
     * @see SinkTestDocument
     * @throws IOException If the target test document could not be generated.
     * @see #isXmlSink()
     * @see #getTestWriter(String)
     * @see #getXmlTestWriter(String)
     */
    public final void testTestDocument() throws IOException
    {
        Writer writer = ( isXmlSink() ? getXmlTestWriter( "testDocument" ) : getTestWriter( "testDocument" ) );
        Sink testSink = createSink( writer );

        try
        {
            SinkTestDocument.generate( testSink );
        }
        finally
        {
            testSink.close();
            IOUtil.close( writer );
        }
    }

    /**
     * Checks that the sequence <code>[title(), text( title ), title_()]</code>,
     * invoked on the current sink, produces the same result as
     * {@link #getTitleBlock getTitleBlock}( title ).
     */
    public void testTitle()
    {
        String title = "Grodek";
        sink.title();
        sink.text( title );
        sink.title_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getTitleBlock( title );

        assertEquals( "Wrong title!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[author(), text( author ), author_()]
     * </code>, invoked on the current sink, produces the same result as
     * {@link #getAuthorBlock getAuthorBlock}( author ).
     */
    public void testAuthor()
    {
        String author = "Georg_Trakl";
        sink.author();
        sink.text( author );
        sink.author_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getAuthorBlock( author );

        assertEquals( "Wrong author!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[date(), text( date ), date_()]</code>,
     * invoked on the current sink, produces the same result as
     * {@link #getDateBlock getDateBlock}( date ).
     */
    public void testDate()
    {
        String date = "1914";
        sink.date();
        sink.text( date );
        sink.date_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getDateBlock( date );

        assertEquals( "Wrong date!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[head(), head_()]</code>,
     * invoked on the current sink, produces the same result as
     * {@link #getHeadBlock getHeadBlock()}.
     */
    public void testHead()
    {
        sink.head();
        sink.head_();
        sink.flush();
        sink.close();

        String actual = normalizeLineEnds( testWriter.toString() );
        String expected = normalizeLineEnds( getHeadBlock() );

        assertEquals( "Wrong head!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[body(), body_()]</code>,
     * invoked on the current sink, produces the same result as
     * {@link #getBodyBlock getBodyBlock()}.
     */
    public void testBody()
    {
        sink.body();
        sink.body_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getBodyBlock();

        assertEquals( "Wrong body!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[article(), article_()]</code>,
     * invoked on the current sink, produces the same result as
     * {@link #getArticleBlock getArticleBlock()}.
     */
    public void testArticle()
    {
        sink.article();
        sink.article_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getArticleBlock();

        assertEquals( "Wrong article!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[navigation(), navigation_()]</code>,
     * invoked on the current sink, produces the same result as
     * {@link #getNavigationBlock getNavigationBlock()}.
     */
    public void testNavigation()
    {
        sink.navigation();
        sink.navigation_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getNavigationBlock();

        assertEquals( "Wrong navigation!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[sidebar(), sidebar_()]</code>,
     * invoked on the current sink, produces the same result as
     * {@link #getSidebarBlock getSidebarBlock()}.
     */
    public void testSidebar()
    {
        sink.sidebar();
        sink.sidebar_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getSidebarBlock();

        assertEquals( "Wrong sidebar!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[sectionTitle(), text( title ),
     * sectionTitle_()]</code>, invoked on the current sink, produces
     * the same result as
     * {@link #getSectionTitleBlock getSectionTitleBlock}( title ).
     */
    public void testSectionTitle()
    {
        String title = "Title";
        sink.sectionTitle();
        sink.text( title );
        sink.sectionTitle_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getSectionTitleBlock( title );

        assertEquals( "Wrong sectionTitle!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[section1(), sectionTitle1(),
     * text( title ), sectionTitle1_(), section1_()]</code>,
     * invoked on the current sink, produces the same result as
     * {@link #getSection1Block getSection1Block}( title ).
     */
    public void testSection1()
    {
        String title = "Title1";
        sink.section1();
        sink.header();
        sink.sectionTitle1();
        sink.text( title );
        sink.sectionTitle1_();
        sink.header_();
        sink.section1_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getSection1Block( title );

        assertEquals( "Wrong section1 block!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[section2(), sectionTitle2(),
     * text( title ), sectionTitle2_(), section2_()]</code>,
     * invoked on the current sink, produces the same result as
     * {@link #getSection2Block getSection2Block}( title ).
     */
    public void testSection2()
    {
        String title = "Title2";
        sink.section2();
        sink.header();
        sink.sectionTitle2();
        sink.text( title );
        sink.sectionTitle2_();
        sink.header_();
        sink.section2_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getSection2Block( title );

        assertEquals( "Wrong section2 block!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[section3(), sectionTitle3(),
     * text( title ), sectionTitle3_(), section3_()]</code>,
     * invoked on the current sink, produces the same result as
     * {@link #getSection3Block getSection3Block}( title ).
     */
    public void testSection3()
    {
        String title = "Title3";
        sink.section3();
        sink.header();
        sink.sectionTitle3();
        sink.text( title );
        sink.sectionTitle3_();
        sink.header_();
        sink.section3_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getSection3Block( title );

        assertEquals( "Wrong section3 block!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[section4(), sectionTitle4(),
     * text( title ), sectionTitle4_(), section4_()]</code>,
     * invoked on the current sink, produces the same result as
     * {@link #getSection4Block getSection4Block}( title ).
     *
     */
    public void testSection4()
    {
        String title = "Title4";
        sink.section4();
        sink.header();
        sink.sectionTitle4();
        sink.text( title );
        sink.sectionTitle4_();
        sink.header_();
        sink.section4_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getSection4Block( title );

        assertEquals( "Wrong section4 block!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[section5(), sectionTitle5(),
     * text( title ), sectionTitle5_(), section5_()]</code>,
     * invoked on the current sink, produces the same result as
     * {@link #getSection5Block getSection5Block}( title ).
     */
    public void testSection5()
    {
        String title = "Title5";
        sink.section5();
        sink.header();
        sink.sectionTitle5();
        sink.text( title );
        sink.sectionTitle5_();
        sink.header_();
        sink.section5_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getSection5Block( title );

        assertEquals( "Wrong section5 block!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[header(), header_()]</code>,
     * invoked on the current sink, produces the same result as
     * {@link #getHeaderBlock getHeaderBlock()}.
     */
    public void testHeader()
    {
        sink.header();
        sink.header_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getHeaderBlock();

        assertEquals( "Wrong header!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[content(), content(), content_(), content_()]</code>,
     * invoked on the current sink, produces the same result as
     * {@link #getContentBlock getContentBlock()}.
     */
    public void testContent()
    {
        sink.content();
        sink.content();
        sink.content_();
        sink.content_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getContentBlock();

        assertEquals( "Wrong content!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[footer(), footer_()]</code>,
     * invoked on the current sink, produces the same result as
     * {@link #getHeaderBlock getHeaderBlock()}.
     */
    public void testFooter()
    {
        sink.footer();
        sink.footer_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getFooterBlock();

        assertEquals( "Wrong footer!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[list(), listItem(), text( item ),
     * listItem_(), list_()]</code>, invoked on the current sink, produces
     * the same result as {@link #getListBlock getListBlock}( item ).
     *
     */
    public void testList()
    {
        String item = "list_item";
        sink.list();
        sink.listItem();
        sink.text( item );
        sink.listItem_();
        sink.list_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getListBlock( item );

        assertEquals( "Wrong list!", expected, actual );
    }

    /**
     * Checks that the sequence <code>
     * [numberedList( Sink.NUMBERING_LOWER_ROMAN ), numberedListItem(),
     * text( item ), numberedListItem_(), numberedList_()]</code>,
     * invoked on the current sink, produces the same result as
     * {@link #getNumberedListBlock getNumberedListBlock}( item ).
     */
    public void testNumberedList()
    {
        String item = "numbered_list_item";
        sink.numberedList( Sink.NUMBERING_LOWER_ROMAN );
        sink.numberedListItem();
        sink.text( item );
        sink.numberedListItem_();
        sink.numberedList_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getNumberedListBlock( item );

        assertEquals( "Wrong numbered list!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[definitionList(), definitionListItem(),
     * definedTerm(), text( definum ), definedTerm_(), definition(),
     * text( definition ), definition_(), definitionListItem_(),
     * definitionList_()]</code>, invoked on the current sink, produces the same
     * result as {@link #getDefinitionListBlock getDefinitionListBlock}
     * ( definum, definition ).
     */
    public void testDefinitionList()
    {
        String definum = "definum";
        String definition = "definition";
        sink.definitionList();
        sink.definitionListItem();
        sink.definedTerm();
        sink.text( definum );
        sink.definedTerm_();
        sink.definition();
        sink.text( definition );
        sink.definition_();
        sink.definitionListItem_();
        sink.definitionList_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getDefinitionListBlock( definum, definition );

        assertEquals( "Wrong definition list!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[figure(), figureGraphics( source ),
     * figureCaption(), text( caption ), figureCaption_(), figure_()]</code>,
     * invoked on the current sink, produces the same result as
     * {@link #getFigureBlock getFigureBlock}( source, caption ).
     */
    public void testFigure()
    {
        String source = "figure.jpg";
        String caption = "Figure_caption";
        sink.figure();
        sink.figureGraphics( source );
        sink.figureCaption();
        sink.text( caption );
        sink.figureCaption_();
        sink.figure_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getFigureBlock( source, caption );

        if ( isXmlSink() )
        {
            assertThat ( wrapXml( actual ), isIdenticalTo( wrapXml( expected ) ));
        }
        else
        {
            assertEquals( actual, expected );
        }
    }


    public void testFigureWithoutCaption()
    {
        String source = "figure.jpg";
        sink.figure();
        sink.figureGraphics( source );
        sink.figure_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getFigureBlock( source, null );

        if ( isXmlSink() )
        {
            assertThat ( wrapXml( actual ), isIdenticalTo( wrapXml( expected ) ));
        }
        else
        {
            assertEquals( actual, expected );
        }
    }
    public void testFigureFromUrl()
    {
        String source = "http://www.gravatar.com/avatar/cdbe99fe3d6af6a18dd8c35b0687a50b?d=mm&s=60";
        sink.figure();
        sink.figureGraphics( source );
        sink.figure_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getFigureBlock( source, null );

        if ( isXmlSink() )
        {
            assertThat ( wrapXml( actual ), isIdenticalTo( wrapXml( expected ) ));
        }
        else
        {
            assertEquals( actual, expected );
        }
    }

    /**
     * Checks that the sequence <code>[table(),
     * tableRows( Sink.JUSTIFY_CENTER, false ), tableRow(), tableCell(),
     * text( cell ), tableCell_(), tableRow_(), tableRows_(), tableCaption(),
     * text( caption ), tableCaption_(), table_()]</code>,
     * invoked on the current sink, produces the same result as
     * {@link #getTableBlock getTableBlock}( cell, caption ).
     */
    public void testTable()
    {
        String cell = "cell";
        String caption = "Table_caption";
        int[] justify = { Sink.JUSTIFY_CENTER };
        sink.table();
        sink.tableRows( justify, false );
        sink.tableRow();
        sink.tableCell();
        sink.text( cell );
        sink.tableCell_();
        sink.tableRow_();
        sink.tableRows_();
        sink.tableCaption();
        sink.text( caption );
        sink.tableCaption_();
        sink.table_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getTableBlock( cell, caption );

        if ( isXmlSink() )
        {
            assertThat ( wrapXml( actual ), isIdenticalTo( wrapXml( expected ) ));
        }
        else
        {
            assertEquals( actual, expected );
        }
    }

    /**
     * Checks that the sequence <code>[paragraph(), text( text ),
     * paragraph_()]</code>, invoked on the current sink, produces
     * the same result as {@link #getParagraphBlock getParagraphBlock}( text ).
     */
    public void testParagraph()
    {
        String text = "Text";
        sink.paragraph();
        sink.text( text );
        sink.paragraph_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getParagraphBlock( text );

        assertEquals( "Wrong paragraph!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[data(), text( text ),
     * data_()]</code>, invoked on the current sink, produces
     * the same result as {@link #getDataBlock getDataBlock}( text ).
     */
    public void testData()
    {
        String value = "Value";
        String text = "Text";
        sink.data( value );
        sink.text( text );
        sink.data_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getDataBlock( value, text );

        assertEquals( "Wrong data!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[time(), text( text ),
     * time_()]</code>, invoked on the current sink, produces
     * the same result as {@link #getTimeBlock getTimeBlock}( text ).
     */
    public void testTime()
    {
        String datetime = "DateTime";
        String text = "Text";
        sink.time( datetime );
        sink.text( text );
        sink.time_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getTimeBlock( datetime, text );

        assertEquals( "Wrong time!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[address(), text( text ),
     * address_()]</code>, invoked on the current sink, produces
     * the same result as {@link #getAddressBlock getAddressBlock}( text ).
     */
    public void testAddress()
    {
        String text = "Text";
        sink.address();
        sink.text( text );
        sink.address_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getAddressBlock( text );

        assertEquals( "Wrong address!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[blockquote(), text( text ),
     * blockquote_()]</code>, invoked on the current sink, produces
     * the same result as {@link #getBlockquoteBlock}( text ).
     */
    public void testBlockquote()
    {
        String text = "Text";
        sink.blockquote();
        sink.text( text );
        sink.blockquote_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getBlockquoteBlock( text );

        assertEquals( "Wrong blockquote!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[division(), text( text ),
     * division_()]</code>, invoked on the current sink, produces
     * the same result as {@link #getDivisionBlock getDivisionBlock}( text ).
     */
    public void testDivider()
    {
        String text = "Text";
        sink.division();
        sink.text( text );
        sink.division_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getDivisionBlock( text );

        assertEquals( "Wrong division!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[verbatim( SinkEventAttributeSet.BOXED ), text( text ),
     * verbatim_()]</code>, invoked on the current sink, produces the
     * same result as {@link #getVerbatimBlock getVerbatimBlock}( text ).
     */
    public void testVerbatim()
    {
        String text = "Text";
        sink.verbatim( SinkEventAttributeSet.BOXED );
        sink.text( text );
        sink.verbatim_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getVerbatimBlock( text );

        assertEquals( "Wrong verbatim!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[horizontalRule()]</code>,
     * invoked on the current sink, produces the same result as
     * {@link #getHorizontalRuleBlock getHorizontalRuleBlock()}.
     */
    public void testHorizontalRule()
    {
        sink.horizontalRule();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getHorizontalRuleBlock();

        assertEquals( "Wrong horizontal rule!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[pageBreak()]</code>,
     * invoked on the current sink, produces the same result as
     * {@link #getPageBreakBlock getPageBreakBlock()}.
     */
    public void testPageBreak()
    {
        sink.pageBreak();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getPageBreakBlock();

        assertEquals( "Wrong pageBreak!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[anchor( anchor ), text( anchor ),
     * anchor_()]</code>, invoked on the current sink, produces the same
     * result as {@link #getAnchorBlock getAnchorBlock}( anchor ).
     */
    public void testAnchor()
    {
        String anchor = "Anchor";
        sink.anchor( anchor );
        sink.text( anchor );
        sink.anchor_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getAnchorBlock( anchor );

        assertEquals( "Wrong anchor!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[link( link ), text( text ),
     * link_()]</code>, invoked on the current sink, produces the same
     * result as {@link #getLinkBlock getLinkBlock}( link, text ).
     */
    public void testLink()
    {
        String link = "#Link";
        String text = "Text";
        sink.link( link );
        sink.text( text );
        sink.link_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getLinkBlock( link, text );

        assertEquals( "Wrong link!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[inline(), text( text ), inline_()]</code>,
     * invoked on the current sink, produces the same result as
     * {@link #getInlineBlock getInlineBlock}( text ).
     */
    public void testInline()
    {
        String text = "Inline";
        sink.inline();
        sink.text( text );
        sink.inline_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getInlineBlock( text );

        assertEquals( "Wrong inline!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[inline(bold), text( text ), inline_()]</code>,
     * invoked on the current sink, produces the same result as
     * {@link #getInlineBoldBlock getInlineBoldBlock}( text ).
     */
    public void testInlineBold()
    {
        String text = "InlineBold";
        sink.inline( SinkEventAttributeSet.Semantics.BOLD );
        sink.text( text );
        sink.inline_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getInlineBoldBlock( text );

        assertEquals( "Wrong inline bold!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[inline(italic), text( text ), inline_()]</code>,
     * invoked on the current sink, produces the same result as
     * {@link #getInlineBoldBlock getInlineBoldBlock}( text ).
     */
    public void testInlineItalic()
    {
        String text = "InlineItalic";
        sink.inline( SinkEventAttributeSet.Semantics.ITALIC );
        sink.text( text );
        sink.inline_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getInlineItalicBlock( text );

        assertEquals( "Wrong inline italic!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[inline(code), text( text ), inline_()]</code>,
     * invoked on the current sink, produces the same result as
     * {@link #getInlineBoldBlock getInlineBoldBlock}( text ).
     */
    public void testInlineCode()
    {
        String text = "InlineCode";
        sink.inline( SinkEventAttributeSet.Semantics.CODE );
        sink.text( text );
        sink.inline_();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getInlineCodeBlock( text );

        assertEquals( "Wrong inline code!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[lineBreak()]</code>,
     * invoked on the current sink, produces the same result as
     * {@link #getLineBreakBlock getLineBreakBlock()}.
     */
    public void testLineBreak()
    {
        sink.lineBreak();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getLineBreakBlock();

        assertEquals( "Wrong lineBreak!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[lineBreakOpportunity()]</code>,
     * invoked on the current sink, produces the same result as
     * {@link #getLineBreakOpportunityBlock getLineBreakOpportunityBlock()}.
     */
    public void testLineBreakOpportunity()
    {
        sink.lineBreakOpportunity();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getLineBreakOpportunityBlock();

        assertEquals( "Wrong lineBreakOpportunity!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[nonBreakingSpace()]</code>,
     * invoked on the current sink, produces the same result as
     * {@link #getNonBreakingSpaceBlock getNonBreakingSpaceBlock()}.
     */
    public void testNonBreakingSpace()
    {
        sink.nonBreakingSpace();
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getNonBreakingSpaceBlock();

        assertEquals( "Wrong nonBreakingSpace!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[text( text )]</code>,
     * invoked on the current sink, produces the same result as
     * {@link #getTextBlock getTextBlock()}.
     */
    public void testText()
    {
        String text = "~,_=,_-,_+,_*,_[,_],_<,_>,_{,_},_\\";
        sink.text( text );
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getTextBlock( text );

        assertEquals( "Wrong text!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[rawText( text )]</code>,
     * invoked on the current sink, produces the same result as
     * {@link #getRawTextBlock getRawTextBlock}( text ).
     */
    public void testRawText()
    {
        String text = "~,_=,_-,_+,_*,_[,_],_<,_>,_{,_},_\\";
        sink.rawText( text );
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getRawTextBlock( text );

        assertEquals( "Wrong rawText!", expected, actual );
    }

    /**
     * Checks that the sequence <code>[comment(comment)]</code>,
     * invoked on the current sink, produces the same result as
     * {@link #getCommentBlock getCommentBlock}( comment ).
     * @since 1.1.1
     */
    public void testComment()
    {
        String comment = "Simple comment with ----";
        sink.comment( comment );
        sink.flush();
        sink.close();

        String actual = testWriter.toString();
        String expected = getCommentBlock( comment );

        assertEquals( "Wrong comment!", expected, actual );

        testWriter.reset();
        sink = createSink( testWriter );
        sink.enableLogging( new PlexusLoggerWrapper( ( ( DefaultPlexusContainer )getContainer() ).getLogger() ) );

        comment = "-";
        sink.comment( comment );
        sink.flush();
        sink.close();

        actual = testWriter.toString();
        expected = getCommentBlock( comment );

        assertEquals( "Wrong comment!", expected, actual );
    }

    // ----------------------------------------------------------------------
    // Utility methods
    // ----------------------------------------------------------------------

    /**
     * Returns the sink that is currently being tested.
     * @return The current test sink.
     */
    protected Sink getSink()
    {
        return sink;
    }

    /**
     * Returns a String representation of all events that have been written to the sink.
     * @return The Sink content as a String.
     */
    protected String getSinkContent()
    {
        return testWriter.toString();
    }

    /**
     * Returns the directory where all sink test output will go.
     * @return The test output directory.
     */
    protected String getOutputDir()
    {
        return "sink/";
    }

    // ----------------------------------------------------------------------
    // Abstract methods the individual SinkTests must provide
    // ----------------------------------------------------------------------

    /**
     * This method allows to use the correct Writer in {@link #testTestDocument()}.
     *
     * @return <code>true</code> if the Sink is an XML one, <code>false</code> otherwise.
     * @see #testTestDocument()
     */
    protected abstract boolean isXmlSink();

    /**
     * Return a new instance of the sink that is being tested.
     * @param writer The writer for the sink.
     * @return A new sink.
     */
    protected abstract Sink createSink( Writer writer );

    /**
     * Returns a title block generated by this sink.
     * @param title The title to use.
     * @return The result of invoking a title block on the current sink.
     * @see #testTitle()
     */
    protected abstract String getTitleBlock( String title );

    /**
     * Returns an author block generated by this sink.
     * @param author The author to use.
     * @return The result of invoking an author block on the current sink.
     * @see #testAuthor()
     */
    protected abstract String getAuthorBlock( String author );

    /**
     * Returns a date block generated by this sink.
     * @param date The date to use.
     * @return The result of invoking a date block on the current sink.
     * @see #testDate()
     */
    protected abstract String getDateBlock( String date );

    /**
     * Returns a head block generated by this sink.
     * @return The result of invoking a head block on the current sink.
     * @see #testHead()
     */
    protected abstract String getHeadBlock();

    /**
     * Returns a body block generated by this sink.
     * @return The result of invoking a body block on the current sink.
     * @see #testBody()
     */
    protected abstract String getBodyBlock();

    /**
     * Returns an article block generated by this sink.
     * @return The result of invoking an article block on the current sink.
     * @see #testArticle()
     */
    protected abstract String getArticleBlock();

    /**
     * Returns an navigation block generated by this sink.
     * @return The result of invoking an navigation block on the current sink.
     * @see #testNavigation()
     */
    protected abstract String getNavigationBlock();

    /**
     * Returns a sidebar block generated by this sink.
     * @return The result of invoking an sidebar block on the current sink.
     * @see #testSidebar()
     */
    protected abstract String getSidebarBlock();

    /**
     * Returns a SectionTitle block generated by this sink.
     * @param title The title to use.
     * @return The result of invoking a SectionTitle block on the current sink.
     * @see #testSectionTitle()
     */
    protected abstract String getSectionTitleBlock( String title );

    /**
     * Returns a Section1 block generated by this sink.
     * @param title The title to use.
     * @return The result of invoking a Section1 block on the current sink.
     * @see #testSection1()
     */
    protected abstract String getSection1Block( String title );

    /**
     * Returns a Section2 block generated by this sink.
     * @param title The title to use.
     * @return The result of invoking a Section2 block on the current sink.
     * @see #testSection2()
     */
    protected abstract String getSection2Block( String title );

    /**
     * Returns a Section3 block generated by this sink.
     * @param title The title to use.
     * @return The result of invoking a Section3 block on the current sink.
     * @see #testSection3()
     */
    protected abstract String getSection3Block( String title );

    /**
     * Returns a Section4 block generated by this sink.
     * @param title The title to use.
     * @return The result of invoking a Section4 block on the current sink.
     * @see #testSection4()
     */
    protected abstract String getSection4Block( String title );

    /**
     * Returns a Section5 block generated by this sink.
     * @param title The title to use.
     * @return The result of invoking a Section5 block on the current sink.
     * @see #testSection5()
     */
    protected abstract String getSection5Block( String title );

    /**
     * Returns a header block generated by this sink.
     * @return The result of invoking a header block on the current sink.
     * @see #testHeader()
     */
    protected abstract String getHeaderBlock();

    /**
     * Returns a content block generated by this sink.
     * @return The result of invoking a content block on the current sink.
     * @see #testContent()
     */
    protected abstract String getContentBlock();

    /**
     * Returns a footer block generated by this sink.
     * @return The result of invoking a footer block on the current sink.
     * @see #testFooter()
     */
    protected abstract String getFooterBlock();

    /**
     * Returns a list block generated by this sink.
     * @param item The item to use.
     * @return The result of invoking a list block on the current sink.
     * @see #testList()
     */
    protected abstract String getListBlock( String item );

    /**
     * Returns a NumberedList block generated by this sink.
     * @param item The item to use.
     * @return The result of invoking a NumberedList block on the current sink.
     * @see #testNumberedList()
     */
    protected abstract String getNumberedListBlock( String item );

    /**
     * Returns a DefinitionList block generated by this sink.
     * @param definum The term to define.
     * @param definition The definition.
     * @return The result of invoking a DefinitionList block on the current sink.
     * @see #testDefinitionList()
     */
    protected abstract String getDefinitionListBlock( String definum,
        String definition );

    /**
     * Returns a Figure block generated by this sink.
     * @param source The figure source string.
     * @param caption The caption to use (may be null).
     * @return The result of invoking a Figure block on the current sink.
     * @see #testFigure()
     */
    protected abstract String getFigureBlock( String source, String caption );

    /**
     * Returns a Table block generated by this sink.
     * @param cell A tabel cell to use.
     * @param caption The caption to use (may be null).
     * @return The result of invoking a Table block on the current sink.
     * @see #testTable()
     */
    protected abstract String getTableBlock( String cell, String caption );

    /**
     * Returns a Paragraph block generated by this sink.
     * @param text The text to use.
     * @return The result of invoking a Paragraph block on the current sink.
     * @see #testParagraph()
     */
    protected abstract String getParagraphBlock( String text );

    /**
     * Returns a Data block generated by this sink.
     * @param value The value to use.
     * @param text The text to use.
     * @return The result of invoking a Data block on the current sink.
     * @see #testData()
     */
    protected abstract String getDataBlock( String value, String text );

    /**
     * Returns a Time block generated by this sink.
     * @param datetime The datetime to use.
     * @param text The text to use.
     * @return The result of invoking a Time block on the current sink.
     * @see #testTime()
     */
    protected abstract String getTimeBlock( String datetime, String text );

    /**
     * Returns an Address block generated by this sink.
     * @param text The text to use.
     * @return The result of invoking an Address block on the current sink.
     * @see #testAddress()
     */
    protected abstract String getAddressBlock( String text );

    /**
     * Returns a Blockquote block generated by this sink.
     * @param text The text to use.
     * @return The result of invoking a Blockquote block on the current sink.
     * @see #testBlockquote()
     */
    protected abstract String getBlockquoteBlock( String text );

    /**
     * Returns a Division block generated by this sink.
     * @param text The text to use.
     * @return The result of invoking a Division block on the current sink.
     * @see #testDivider()
     */
    protected abstract String getDivisionBlock( String text );

    /**
     * Returns a Verbatim block generated by this sink.
     * @param text The text to use.
     * @return The result of invoking a Verbatim block on the current sink.
     * @see #testVerbatim()
     */
    protected abstract String getVerbatimBlock( String text );

    /**
     * Returns a HorizontalRule block generated by this sink.
     * @return The result of invoking a HorizontalRule block on the current sink.
     * @see #testHorizontalRule()
     */
    protected abstract String getHorizontalRuleBlock();

    /**
     * Returns a PageBreak block generated by this sink.
     * @return The result of invoking a PageBreak block on the current sink.
     * @see #testPageBreak()
     */
    protected abstract String getPageBreakBlock();

    /**
     * Returns a Anchor block generated by this sink.
     * @param anchor The anchor to use.
     * @return The result of invoking a Anchor block on the current sink.
     * @see #testAnchor()
     */
    protected abstract String getAnchorBlock( String anchor );

    /**
     * Returns a Link block generated by this sink.
     * @param link The link to use.
     * @param text The link text.
     * @return The result of invoking a Link block on the current sink.
     * @see #testLink()
     */
    protected abstract String getLinkBlock( String link, String text );

    /**
     * Returns an Inline block generated by this sink.
     * @param text The text to use.
     * @return The result of invoking a Inline block on the current sink.
     * @see #testInline()
     */
    protected abstract String getInlineBlock( String text );

    /**
     * Returns an Inline italic block generated by this sink.
     * @param text The text to use.
     * @return The result of invoking a Inline italic block on the current sink.
     * @see #testInlineItalic()
     */
    protected abstract String getInlineItalicBlock( String text );

    /**
     * Returns an Inline bold block generated by this sink.
     * @param text The text to use.
     * @return The result of invoking a Inline bold block on the current sink.
     * @see #testInlineBold()
     */
    protected abstract String getInlineBoldBlock( String text );

    /**
     * Returns an Inline code block generated by this sink.
     * @param text The text to use.
     * @return The result of invoking a Inline code block on the current sink.
     * @see #testInlineBold()
     */
    protected abstract String getInlineCodeBlock( String text );

    /**
     * Returns a LineBreak block generated by this sink.
     * @return The result of invoking a LineBreak block on the current sink.
     * @see #testLineBreak()
     */
    protected abstract String getLineBreakBlock();

    /**
     * Returns a LineBreakOpportunity block generated by this sink.
     * @return The result of invoking a LineBreakOpportunity block on the
     * current sink.
     * @see #testLineBreakOpportunity()
     */
    protected abstract String getLineBreakOpportunityBlock();

    /**
     * Returns a NonBreakingSpace block generated by this sink.
     * @return The result of invoking a NonBreakingSpace block
     * on the current sink.
     * @see #testNonBreakingSpace()
     */
    protected abstract String getNonBreakingSpaceBlock();

    /**
     * Returns a Text block generated by this sink.
     * @param text The text to use.
     * @return The result of invoking a Text block on the current sink.
     * @see #testText()
     */
    protected abstract String getTextBlock( String text );

    /**
     * Returns a RawText block generated by this sink.
     * @param text The text to use.
     * @return The result of invoking a RawText block on the current sink.
     * @see #testRawText()
     */
    protected abstract String getRawTextBlock( String text );

    /**
     * Returns a comment block generated by this sink.
     * @param text The text to use.
     * @return The result of invoking a comment block on the current sink.
     * @see #testComment()
     * @since 1.1.1
     */
    protected abstract String getCommentBlock( String text );

    protected final void verifyValignSup( String text )
    {
        sink.text( "ValignSup", new SinkEventAttributeSet( SinkEventAttributes.VALIGN, "sup"  ) );
        sink.flush();
        sink.close();

        String actual = testWriter.toString();

        assertEquals( "Wrong valign sup!", text, actual );
    }

    protected final void verifyValignSub( String text )
    {
        sink.text( "ValignSub", new SinkEventAttributeSet( SinkEventAttributes.VALIGN, "sub"  ) );
        sink.flush();
        sink.close();

        String actual = testWriter.toString();

        assertEquals( "Wrong valign sub!", text, actual );
    }

    protected final void verifyDecorationUnderline( String text )
    {
        sink.text( "DecorationUnderline", new SinkEventAttributeSet( SinkEventAttributes.DECORATION, "underline"  ) );
        sink.flush();
        sink.close();

        String actual = testWriter.toString();

        assertEquals( "Wrong decoration underline!", text, actual );
    }

    protected final void verifyDecorationLineThrough( String text )
    {
        sink.text( "DecorationLineThrough", new SinkEventAttributeSet( SinkEventAttributes.DECORATION, "line-through"  ) );
        sink.flush();
        sink.close();

        String actual = testWriter.toString();

        assertEquals( "Wrong decoration line-through!", text, actual );
    }


}
