You are viewing a plain text version of this content. The canonical link for it is here.
Posted to doxia-commits@maven.apache.org by vs...@apache.org on 2008/03/12 12:26:39 UTC

svn commit: r636283 - /maven/doxia/doxia/trunk/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptParser.java

Author: vsiveton
Date: Wed Mar 12 04:26:31 2008
New Revision: 636283

URL: http://svn.apache.org/viewvc?rev=636283&view=rev
Log:
o increased visibility of some methods/fields
o improved the readingness

Modified:
    maven/doxia/doxia/trunk/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptParser.java

Modified: maven/doxia/doxia/trunk/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptParser.java
URL: http://svn.apache.org/viewvc/maven/doxia/doxia/trunk/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptParser.java?rev=636283&r1=636282&r2=636283&view=diff
==============================================================================
--- maven/doxia/doxia/trunk/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptParser.java (original)
+++ maven/doxia/doxia/trunk/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptParser.java Wed Mar 12 04:26:31 2008
@@ -125,7 +125,7 @@
         "COMMENT_BLOCK" };
 
     /** An array of spaces. */
-    private static final char SPACES[] = {
+    protected static final char SPACES[] = {
         ' ',
         ' ',
         ' ',
@@ -219,18 +219,9 @@
     // Instance fields
     // ----------------------------------------------------------------------
 
-    /** sourceContent. */
-    private String sourceContent;
-
     /** the AptSource. */
     private AptSource source;
 
-    /** the sink to receive the events. */
-    private Sink sink;
-
-    /** a line of AptSource. */
-    private String line;
-
     /** a block of AptSource. */
     private Block block;
 
@@ -240,6 +231,15 @@
     /** blockLineNumber. */
     private int blockLineNumber;
 
+    /** sourceContent. */
+    protected String sourceContent;
+
+    /** the sink to receive the events. */
+    protected Sink sink;
+
+    /** a line of AptSource. */
+    protected String line;
+
     // ----------------------------------------------------------------------
     // Public methods
     // ----------------------------------------------------------------------
@@ -313,208 +313,611 @@
     }
 
     // ----------------------------------------------------------------------
-    // Private methods
+    // Protected methods
     // ----------------------------------------------------------------------
 
     /**
-     * Parse the head of the Apt source document.
+     * Parse the next line of the Apt source document.
      *
      * @throws AptParseException if something goes wrong.
      */
-    private void traverseHead()
+    protected void nextLine()
         throws AptParseException
     {
-        sink.head();
-
-        if ( block != null && block.getType() == TITLE )
-        {
-            block.traverse();
-            nextBlock();
-        }
-
-        sink.head_();
+        line = source.getNextLine();
     }
 
     /**
-     * Parse the body of the Apt source document.
+     * Parse the given text.
      *
+     * @param text the text to parse.
+     * @param begin offset.
+     * @param end offset.
+     * @param sink the sink to receive the events.
      * @throws AptParseException if something goes wrong.
      */
-    private void traverseBody()
+    protected void doTraverseText( String text, int begin, int end, Sink sink )
         throws AptParseException
     {
-        sink.body();
-
-        if ( block != null )
-        {
-            traverseSectionBlocks();
-        }
+        boolean anchor = false;
+        boolean link = false;
+        boolean italic = false;
+        boolean bold = false;
+        boolean monospaced = false;
+        StringBuffer buffer = new StringBuffer( end - begin );
 
-        while ( block != null )
+        for ( int i = begin; i < end; ++i )
         {
-            traverseSection( 0 );
-        }
+            char c = text.charAt( i );
+            switch ( c )
+            {
+                case BACKSLASH:
+                    if ( i + 1 < end )
+                    {
+                        char escaped = text.charAt( i + 1 );
+                        switch ( escaped )
+                        {
+                            case SPACE:
+                                ++i;
+                                flushTraversed( buffer, sink );
+                                sink.nonBreakingSpace();
+                                break;
+                            case '\r':
+                            case '\n':
+                                ++i;
+                                // Skip white space which may follow a line break.
+                                while ( i + 1 < end && Character.isWhitespace( text.charAt( i + 1 ) ) )
+                                {
+                                    ++i;
+                                }
+                                flushTraversed( buffer, sink );
+                                sink.lineBreak();
+                                break;
+                            case BACKSLASH:
+                            case PIPE:
+                            case COMMENT:
+                            case EQUAL:
+                            case MINUS:
+                            case PLUS:
+                            case STAR:
+                            case LEFT_SQUARE_BRACKET:
+                            case RIGHT_SQUARE_BRACKET:
+                            case LESS_THAN:
+                            case GREATER_THAN:
+                            case LEFT_CURLY_BRACKET:
+                            case RIGHT_CURLY_BRACKET:
+                                ++i;
+                                buffer.append( escaped );
+                                break;
+                            case 'x':
+                                if ( i + 3 < end && isHexChar( text.charAt( i + 2 ) )
+                                    && isHexChar( text.charAt( i + 3 ) ) )
+                                {
+                                    int value = '?';
+                                    try
+                                    {
+                                        value = Integer.parseInt( text.substring( i + 2, i + 4 ), 16 );
+                                    }
+                                    catch ( NumberFormatException e )
+                                    {
+                                        if ( getLog().isDebugEnabled() )
+                                        {
+                                            getLog().debug( "Not a number: " + text.substring( i + 2, i + 4 ) );
+                                        }
+                                    }
 
-        sink.body_();
-    }
+                                    i += 3;
+                                    buffer.append( (char) value );
+                                }
+                                else
+                                {
+                                    buffer.append( BACKSLASH );
+                                }
+                                break;
+                            case 'u':
+                                if ( i + 5 < end && isHexChar( text.charAt( i + 2 ) )
+                                    && isHexChar( text.charAt( i + 3 ) ) && isHexChar( text.charAt( i + 4 ) )
+                                    && isHexChar( text.charAt( i + 5 ) ) )
+                                {
+                                    int value = '?';
+                                    try
+                                    {
+                                        value = Integer.parseInt( text.substring( i + 2, i + 6 ), 16 );
+                                    }
+                                    catch ( NumberFormatException e )
+                                    {
+                                        if ( getLog().isDebugEnabled() )
+                                        {
+                                            getLog().debug( "Not a number: " + text.substring( i + 2, i + 6 ) );
+                                        }
+                                    }
 
-    /**
-     * Parse a section of the Apt source document.
-     *
-     * @param level The section level.
-     * @throws AptParseException if something goes wrong.
-     */
-    private void traverseSection( int level )
-        throws AptParseException
-    {
-        if ( block == null )
-        {
-            return;
-        }
+                                    i += 5;
+                                    buffer.append( (char) value );
+                                }
+                                else
+                                {
+                                    buffer.append( BACKSLASH );
+                                }
+                                break;
+                            default:
+                                if ( isOctalChar( escaped ) )
+                                {
+                                    int octalChars = 1;
+                                    if ( isOctalChar( charAt( text, end, i + 2 ) ) )
+                                    {
+                                        ++octalChars;
+                                        if ( isOctalChar( charAt( text, end, i + 3 ) ) )
+                                        {
+                                            ++octalChars;
+                                        }
+                                    }
+                                    int value = '?';
+                                    try
+                                    {
+                                        value = Integer.parseInt( text.substring( i + 1, i + 1 + octalChars ), 8 );
+                                    }
+                                    catch ( NumberFormatException e )
+                                    {
+                                        if ( getLog().isDebugEnabled() )
+                                        {
+                                            getLog().debug(
+                                                            "Not a number: "
+                                                                + text.substring( i + 1, i + 1 + octalChars ) );
+                                        }
+                                    }
 
-        int type = SECTION1 + level;
+                                    i += octalChars;
+                                    buffer.append( (char) value );
+                                }
+                                else
+                                {
+                                    buffer.append( BACKSLASH );
+                                }
+                        }
+                    }
+                    else
+                    {
+                        buffer.append( BACKSLASH );
+                    }
+                    break;
 
-        expectedBlock( type );
+                case LEFT_CURLY_BRACKET: /*}*/
+                    if ( !anchor && !link )
+                    {
+                        if ( i + 1 < end && text.charAt( i + 1 ) == LEFT_CURLY_BRACKET /*}*/)
+                        {
+                            ++i;
+                            link = true;
+                            flushTraversed( buffer, sink );
 
-        switch ( level )
-        {
-            case 0:
-                sink.section1();
-                break;
-            case 1:
-                sink.section2();
-                break;
-            case 2:
-                sink.section3();
-                break;
-            case 3:
-                sink.section4();
-                break;
-            case 4:
-                sink.section5();
-                break;
-            default:
-                break;
-        }
+                            String linkAnchor = null;
 
-        block.traverse();
+                            if ( i + 1 < end && text.charAt( i + 1 ) == LEFT_CURLY_BRACKET /*}*/)
+                            {
+                                ++i;
+                                StringBuffer buf = new StringBuffer();
+                                i = skipTraversedLinkAnchor( text, i + 1, end, buf );
+                                linkAnchor = buf.toString();
+                            }
 
-        nextBlock();
+                            if ( linkAnchor == null )
+                            {
+                                linkAnchor = getTraversedLink( text, i + 1, end );
+                            }
 
-        traverseSectionBlocks();
+                            sink.link( linkAnchor );
+                        }
+                        else
+                        {
+                            anchor = true;
+                            flushTraversed( buffer, sink );
+                            sink.anchor( getTraversedAnchor( text, i + 1, end ) );
+                        }
+                    }
+                    else
+                    {
+                        buffer.append( c );
+                    }
+                    break;
 
-        while ( block != null )
-        {
-            if ( block.getType() <= type )
-            {
-                break;
-            }
+                case /*{*/RIGHT_CURLY_BRACKET:
+                    if ( link && i + 1 < end && text.charAt( i + 1 ) == /*{*/RIGHT_CURLY_BRACKET )
+                    {
+                        ++i;
+                        link = false;
+                        flushTraversed( buffer, sink );
+                        sink.link_();
+                    }
+                    else if ( anchor )
+                    {
+                        anchor = false;
+                        flushTraversed( buffer, sink );
+                        sink.anchor_();
+                    }
+                    else
+                    {
+                        buffer.append( c );
+                    }
+                    break;
 
-            traverseSection( level + 1 );
+                case LESS_THAN:
+                    if ( !italic && !bold && !monospaced )
+                    {
+                        if ( i + 1 < end && text.charAt( i + 1 ) == LESS_THAN )
+                        {
+                            if ( i + 2 < end && text.charAt( i + 2 ) == LESS_THAN )
+                            {
+                                i += 2;
+                                monospaced = true;
+                                flushTraversed( buffer, sink );
+                                sink.monospaced();
+                            }
+                            else
+                            {
+                                ++i;
+                                bold = true;
+                                flushTraversed( buffer, sink );
+                                sink.bold();
+                            }
+                        }
+                        else
+                        {
+                            italic = true;
+                            flushTraversed( buffer, sink );
+                            sink.italic();
+                        }
+                    }
+                    else
+                    {
+                        buffer.append( c );
+                    }
+                    break;
+
+                case GREATER_THAN:
+                    if ( monospaced && i + 2 < end && text.charAt( i + 1 ) == GREATER_THAN
+                        && text.charAt( i + 2 ) == GREATER_THAN )
+                    {
+                        i += 2;
+                        monospaced = false;
+                        flushTraversed( buffer, sink );
+                        sink.monospaced_();
+                    }
+                    else if ( bold && i + 1 < end && text.charAt( i + 1 ) == GREATER_THAN )
+                    {
+                        ++i;
+                        bold = false;
+                        flushTraversed( buffer, sink );
+                        sink.bold_();
+                    }
+                    else if ( italic )
+                    {
+                        italic = false;
+                        flushTraversed( buffer, sink );
+                        sink.italic_();
+                    }
+                    else
+                    {
+                        buffer.append( c );
+                    }
+                    break;
+
+                default:
+                    if ( Character.isWhitespace( c ) )
+                    {
+                        buffer.append( SPACE );
+
+                        // Skip to the last char of a sequence of white spaces.
+                        while ( i + 1 < end && Character.isWhitespace( text.charAt( i + 1 ) ) )
+                        {
+                            ++i;
+                        }
+                    }
+                    else
+                    {
+                        buffer.append( c );
+                    }
+            }
         }
 
-        switch ( level )
+        if ( monospaced )
         {
-            case 0:
-                sink.section1_();
-                break;
-            case 1:
-                sink.section2_();
-                break;
-            case 2:
-                sink.section3_();
-                break;
-            case 3:
-                sink.section4_();
-                break;
-            case 4:
-                sink.section5_();
-                break;
-            default:
-                break;
+            throw new AptParseException( "missing '" + MONOSPACED_END_MARKUP + "'" );
+        }
+        if ( bold )
+        {
+            throw new AptParseException( "missing '" + BOLD_END_MARKUP + "'" );
+        }
+        if ( italic )
+        {
+            throw new AptParseException( "missing '" + ITALIC_END_MARKUP + "'" );
         }
+        if ( link )
+        {
+            throw new AptParseException( "missing '" + LINK_END_MARKUP + "'" );
+        }
+        if ( anchor )
+        {
+            throw new AptParseException( "missing '" + ANCHOR_END_MARKUP + "'" );
+        }
+
+        flushTraversed( buffer, sink );
     }
 
+    // -----------------------------------------------------------------------
+
     /**
-     * Parse the section blocks of the Apt source document.
+     * Returns the character at position i of the given string.
      *
-     * @throws AptParseException if something goes wrong.
+     * @param string the string.
+     * @param length length.
+     * @param i offset.
+     * @return the character, or '\0' if i > length.
      */
-    private void traverseSectionBlocks()
-        throws AptParseException
+    protected static char charAt( String string, int length, int i )
     {
-        loop: while ( block != null )
+        return ( i < length ) ? string.charAt( i ) : '\0';
+    }
+
+    /**
+     * Skip spaces.
+     *
+     * @param string string.
+     * @param length length.
+     * @param i offset.
+     * @return int.
+     */
+    protected static int skipSpace( String string, int length, int i )
+    {
+        loop: for ( ; i < length; ++i )
         {
-            switch ( block.getType() )
+            switch ( string.charAt( i ) )
             {
-                case PARAGRAPH:
-                case VERBATIM:
-                case FIGURE:
-                case TABLE:
-                case HORIZONTAL_RULE:
-                case PG_BREAK:
-                case MACRO:
-                case COMMENT_BLOCK:
-                    block.traverse();
-                    nextBlock();
-                    break;
-
-                case LIST_ITEM:
-                    traverseList();
-                    break;
-
-                case NUMBERED_LIST_ITEM:
-                    traverseNumberedList();
-                    break;
-
-                case DEFINITION_LIST_ITEM:
-                    traverseDefinitionList();
-                    break;
-
-                case LIST_BREAK:
-                    // May be this is a list break which has not been indented
-                    // very precisely.
-                    nextBlock();
+                case SPACE:
+                case TAB:
                     break;
-
                 default:
-                    // A section block which starts a new section.
                     break loop;
             }
         }
+        return i;
     }
 
     /**
-     * Parse a list of the Apt source document.
+     * Replace part of a string.
      *
-     * @throws AptParseException if something goes wrong.
+     * @param string the string
+     * @param oldSub the substring to replace
+     * @param newSub the replacement string
+     * @return String
      */
-    private void traverseList()
-        throws AptParseException
+    protected static String replaceAll( String string, String oldSub, String newSub )
     {
-        if ( block == null )
+        StringBuffer replaced = new StringBuffer();
+        int oldSubLength = oldSub.length();
+        int begin, end;
+
+        begin = 0;
+        while ( ( end = string.indexOf( oldSub, begin ) ) >= 0 )
         {
-            return;
+            if ( end > begin )
+            {
+                replaced.append( string.substring( begin, end ) );
+            }
+            replaced.append( newSub );
+            begin = end + oldSubLength;
+        }
+        if ( begin < string.length() )
+        {
+            replaced.append( string.substring( begin ) );
         }
 
-        expectedBlock( LIST_ITEM );
-
-        int listIndent = block.getIndent();
-
-        sink.list();
-
-        sink.listItem();
+        return replaced.toString();
+    }
 
-        block.traverse();
+    // ----------------------------------------------------------------------
+    // Private methods
+    // ----------------------------------------------------------------------
 
-        nextBlock();
+    /**
+     * Parse the head of the Apt source document.
+     *
+     * @throws AptParseException if something goes wrong.
+     */
+    private void traverseHead()
+        throws AptParseException
+    {
+        sink.head();
 
-        loop: while ( block != null )
+        if ( block != null && block.getType() == TITLE )
         {
-            int blockIndent = block.getIndent();
+            block.traverse();
+            nextBlock();
+        }
 
-            switch ( block.getType() )
-            {
+        sink.head_();
+    }
+
+    /**
+     * Parse the body of the Apt source document.
+     *
+     * @throws AptParseException if something goes wrong.
+     */
+    private void traverseBody()
+        throws AptParseException
+    {
+        sink.body();
+
+        if ( block != null )
+        {
+            traverseSectionBlocks();
+        }
+
+        while ( block != null )
+        {
+            traverseSection( 0 );
+        }
+
+        sink.body_();
+    }
+
+    /**
+     * Parse a section of the Apt source document.
+     *
+     * @param level The section level.
+     * @throws AptParseException if something goes wrong.
+     */
+    private void traverseSection( int level )
+        throws AptParseException
+    {
+        if ( block == null )
+        {
+            return;
+        }
+
+        int type = SECTION1 + level;
+
+        expectedBlock( type );
+
+        switch ( level )
+        {
+            case 0:
+                sink.section1();
+                break;
+            case 1:
+                sink.section2();
+                break;
+            case 2:
+                sink.section3();
+                break;
+            case 3:
+                sink.section4();
+                break;
+            case 4:
+                sink.section5();
+                break;
+            default:
+                break;
+        }
+
+        block.traverse();
+
+        nextBlock();
+
+        traverseSectionBlocks();
+
+        while ( block != null )
+        {
+            if ( block.getType() <= type )
+            {
+                break;
+            }
+
+            traverseSection( level + 1 );
+        }
+
+        switch ( level )
+        {
+            case 0:
+                sink.section1_();
+                break;
+            case 1:
+                sink.section2_();
+                break;
+            case 2:
+                sink.section3_();
+                break;
+            case 3:
+                sink.section4_();
+                break;
+            case 4:
+                sink.section5_();
+                break;
+            default:
+                break;
+        }
+    }
+
+    /**
+     * Parse the section blocks of the Apt source document.
+     *
+     * @throws AptParseException if something goes wrong.
+     */
+    private void traverseSectionBlocks()
+        throws AptParseException
+    {
+        loop: while ( block != null )
+        {
+            switch ( block.getType() )
+            {
+                case PARAGRAPH:
+                case VERBATIM:
+                case FIGURE:
+                case TABLE:
+                case HORIZONTAL_RULE:
+                case PG_BREAK:
+                case MACRO:
+                case COMMENT_BLOCK:
+                    block.traverse();
+                    nextBlock();
+                    break;
+
+                case LIST_ITEM:
+                    traverseList();
+                    break;
+
+                case NUMBERED_LIST_ITEM:
+                    traverseNumberedList();
+                    break;
+
+                case DEFINITION_LIST_ITEM:
+                    traverseDefinitionList();
+                    break;
+
+                case LIST_BREAK:
+                    // May be this is a list break which has not been indented
+                    // very precisely.
+                    nextBlock();
+                    break;
+
+                default:
+                    // A section block which starts a new section.
+                    break loop;
+            }
+        }
+    }
+
+    /**
+     * Parse a list of the Apt source document.
+     *
+     * @throws AptParseException if something goes wrong.
+     */
+    private void traverseList()
+        throws AptParseException
+    {
+        if ( block == null )
+        {
+            return;
+        }
+
+        expectedBlock( LIST_ITEM );
+
+        int listIndent = block.getIndent();
+
+        sink.list();
+
+        sink.listItem();
+
+        block.traverse();
+
+        nextBlock();
+
+        loop: while ( block != null )
+        {
+            int blockIndent = block.getIndent();
+
+            switch ( block.getType() )
+            {
                 case PARAGRAPH:
                     if ( blockIndent < listIndent )
                     {
@@ -774,17 +1177,6 @@
     }
 
     /**
-     * Parse the next line of the Apt source document.
-     *
-     * @throws AptParseException if something goes wrong.
-     */
-    private void nextLine()
-        throws AptParseException
-    {
-        line = source.getNextLine();
-    }
-
-    /**
      * Parse the next block of the Apt source document.
      *
      * @throws AptParseException if something goes wrong.
@@ -836,551 +1228,197 @@
             {
                 nextLine();
             }
-        }
-
-        blockFileName = source.getName();
-        blockLineNumber = source.getLineNumber();
-        block = null;
-        switch ( line.charAt( i ) )
-        {
-            case STAR:
-                if ( indent == 0 )
-                {
-                    if ( charAt( line, length, i + 1 ) == MINUS && charAt( line, length, i + 2 ) == MINUS )
-                    {
-                        block = new Table( indent, line );
-                    }
-                    else if ( charAt( line, length, i + 1 ) == STAR )
-                    {
-                        if ( charAt( line, length, i + 2 ) == STAR )
-                        {
-                            if ( charAt( line, length, i + 3 ) == STAR )
-                            {
-                                block = new Section5( indent, line );
-                            }
-                            else
-                            {
-                                block = new Section4( indent, line );
-                            }
-                        }
-                        else
-                        {
-                            block = new Section3( indent, line );
-                        }
-                    }
-                    else
-                    {
-                        block = new Section2( indent, line );
-                    }
-                }
-                else
-                {
-                    block = new ListItem( indent, line );
-                }
-                break;
-            case LEFT_SQUARE_BRACKET:
-                if ( charAt( line, length, i + 1 ) == RIGHT_SQUARE_BRACKET )
-                {
-                    block = new ListBreak( indent, line );
-                }
-                else
-                {
-                    if ( indent == 0 )
-                    {
-                        block = new Figure( indent, line );
-                    }
-                    else
-                    {
-                        if ( charAt( line, length, i + 1 ) == LEFT_SQUARE_BRACKET )
-                        {
-                            int numbering;
-
-                            switch ( charAt( line, length, i + 2 ) )
-                            {
-                                case NUMBERING_LOWER_ALPHA_CHAR:
-                                    numbering = Sink.NUMBERING_LOWER_ALPHA;
-                                    break;
-                                case NUMBERING_UPPER_ALPHA_CHAR:
-                                    numbering = Sink.NUMBERING_UPPER_ALPHA;
-                                    break;
-                                case NUMBERING_LOWER_ROMAN_CHAR:
-                                    numbering = Sink.NUMBERING_LOWER_ROMAN;
-                                    break;
-                                case NUMBERING_UPPER_ROMAN_CHAR:
-                                    numbering = Sink.NUMBERING_UPPER_ROMAN;
-                                    break;
-                                case NUMBERING:
-                                default:
-                                    // The first item establishes the numbering
-                                    // scheme for the whole list.
-                                    numbering = Sink.NUMBERING_DECIMAL;
-                            }
-
-                            block = new NumberedListItem( indent, line, numbering );
-                        }
-                        else
-                        {
-                            block = new DefinitionListItem( indent, line );
-                        }
-                    }
-                }
-                break;
-            case MINUS:
-                if ( charAt( line, length, i + 1 ) == MINUS && charAt( line, length, i + 2 ) == MINUS )
-                {
-                    if ( indent == 0 )
-                    {
-                        block = new Verbatim( indent, line );
-                    }
-                    else
-                    {
-                        if ( firstBlock )
-                        {
-                            block = new Title( indent, line );
-                        }
-                    }
-                }
-                break;
-            case PLUS:
-                if ( indent == 0 && charAt( line, length, i + 1 ) == MINUS && charAt( line, length, i + 2 ) == MINUS )
-                {
-                    block = new Verbatim( indent, line );
-                }
-                break;
-            case EQUAL:
-                if ( indent == 0 && charAt( line, length, i + 1 ) == EQUAL && charAt( line, length, i + 2 ) == EQUAL )
-                {
-                    block = new HorizontalRule( indent, line );
-                }
-                break;
-            case PAGE_BREAK:
-                if ( indent == 0 )
-                {
-                    block = new PageBreak( indent, line );
-                }
-                break;
-            case PERCENT:
-                if ( indent == 0 && charAt( line, length, i + 1 ) == LEFT_CURLY_BRACKET )
-                {
-                    block = new MacroBlock( indent, line );
-                }
-                break;
-            case COMMENT:
-                if ( indent == 0 && charAt( line, length, i + 1 ) == COMMENT )
-                {
-                    block = new Comment( line.substring( 2 ).trim() );
-                }
-                break;
-            default:
-                break;
-        }
-
-        if ( block == null )
-        {
-            if ( indent == 0 )
-            {
-                block = new Section1( indent, line );
-            }
-            else
-            {
-                block = new Paragraph( indent, line );
-            }
-        }
-    }
-
-    /**
-     * Checks that the current block is of the expected type.
-     *
-     * @param type the expected type.
-     * @throws AptParseException if something goes wrong.
-     */
-    private void expectedBlock( int type )
-        throws AptParseException
-    {
-        int blockType = block.getType();
-
-        if ( blockType != type )
-        {
-            throw new AptParseException( "expected " + TYPE_NAMES[type] + ", found " + TYPE_NAMES[blockType] );
-        }
-    }
-
-    // -----------------------------------------------------------------------
-
-    /**
-     * Determin if c is an octal character.
-     *
-     * @param c the character.
-     * @return boolean
-     */
-    private static boolean isOctalChar( char c )
-    {
-        return ( c >= '0' && c <= '7' );
-    }
-
-    /**
-     * Determin if c is an hex character.
-     *
-     * @param c the character.
-     * @return boolean
-     */
-    private static boolean isHexChar( char c )
-    {
-        return ( ( c >= '0' && c <= '9' ) || ( c >= 'a' && c <= 'f' ) || ( c >= 'A' && c <= 'F' ) );
-    }
-
-    /**
-     * Returns the character at position i of the given string.
-     *
-     * @param string the string.
-     * @param length length.
-     * @param i offset.
-     * @return the character, or '\0' if i > length.
-     */
-    private static char charAt( String string, int length, int i )
-    {
-        return ( i < length ) ? string.charAt( i ) : '\0';
-    }
-
-    /**
-     * Skip spaces.
-     *
-     * @param string string.
-     * @param length length.
-     * @param i offset.
-     * @return int.
-     */
-    private static int skipSpace( String string, int length, int i )
-    {
-        loop: for ( ; i < length; ++i )
-        {
-            switch ( string.charAt( i ) )
-            {
-                case SPACE:
-                case TAB:
-                    break;
-                default:
-                    break loop;
-            }
-        }
-        return i;
-    }
-
-    /**
-     * Parse the given text.
-     *
-     * @param text the text to parse.
-     * @param begin offset.
-     * @param end offset.
-     * @param sink the sink to receive the events.
-     * @throws AptParseException if something goes wrong.
-     */
-    private void doTraverseText( String text, int begin, int end, Sink sink )
-        throws AptParseException
-    {
-        boolean anchor = false;
-        boolean link = false;
-        boolean italic = false;
-        boolean bold = false;
-        boolean monospaced = false;
-        StringBuffer buffer = new StringBuffer( end - begin );
-
-        for ( int i = begin; i < end; ++i )
-        {
-            char c = text.charAt( i );
-            switch ( c )
-            {
-                case BACKSLASH:
-                    if ( i + 1 < end )
-                    {
-                        char escaped = text.charAt( i + 1 );
-                        switch ( escaped )
-                        {
-                            case SPACE:
-                                ++i;
-                                flushTraversed( buffer, sink );
-                                sink.nonBreakingSpace();
-                                break;
-                            case '\r':
-                            case '\n':
-                                ++i;
-                                // Skip white space which may follow a line break.
-                                while ( i + 1 < end && Character.isWhitespace( text.charAt( i + 1 ) ) )
-                                {
-                                    ++i;
-                                }
-                                flushTraversed( buffer, sink );
-                                sink.lineBreak();
-                                break;
-                            case BACKSLASH:
-                            case PIPE:
-                            case COMMENT:
-                            case EQUAL:
-                            case MINUS:
-                            case PLUS:
-                            case STAR:
-                            case LEFT_SQUARE_BRACKET:
-                            case RIGHT_SQUARE_BRACKET:
-                            case LESS_THAN:
-                            case GREATER_THAN:
-                            case LEFT_CURLY_BRACKET:
-                            case RIGHT_CURLY_BRACKET:
-                                ++i;
-                                buffer.append( escaped );
-                                break;
-                            case 'x':
-                                if ( i + 3 < end && isHexChar( text.charAt( i + 2 ) )
-                                    && isHexChar( text.charAt( i + 3 ) ) )
-                                {
-                                    int value = '?';
-                                    try
-                                    {
-                                        value = Integer.parseInt( text.substring( i + 2, i + 4 ), 16 );
-                                    }
-                                    catch ( NumberFormatException e )
-                                    {
-                                        if ( getLog().isDebugEnabled() )
-                                        {
-                                            getLog().debug( "Not a number: " + text.substring( i + 2, i + 4 ) );
-                                        }
-                                    }
-
-                                    i += 3;
-                                    buffer.append( (char) value );
-                                }
-                                else
-                                {
-                                    buffer.append( BACKSLASH );
-                                }
-                                break;
-                            case 'u':
-                                if ( i + 5 < end && isHexChar( text.charAt( i + 2 ) )
-                                    && isHexChar( text.charAt( i + 3 ) ) && isHexChar( text.charAt( i + 4 ) )
-                                    && isHexChar( text.charAt( i + 5 ) ) )
-                                {
-                                    int value = '?';
-                                    try
-                                    {
-                                        value = Integer.parseInt( text.substring( i + 2, i + 6 ), 16 );
-                                    }
-                                    catch ( NumberFormatException e )
-                                    {
-                                        if ( getLog().isDebugEnabled() )
-                                        {
-                                            getLog().debug( "Not a number: " + text.substring( i + 2, i + 6 ) );
-                                        }
-                                    }
-
-                                    i += 5;
-                                    buffer.append( (char) value );
-                                }
-                                else
-                                {
-                                    buffer.append( BACKSLASH );
-                                }
-                                break;
-                            default:
-                                if ( isOctalChar( escaped ) )
-                                {
-                                    int octalChars = 1;
-                                    if ( isOctalChar( charAt( text, end, i + 2 ) ) )
-                                    {
-                                        ++octalChars;
-                                        if ( isOctalChar( charAt( text, end, i + 3 ) ) )
-                                        {
-                                            ++octalChars;
-                                        }
-                                    }
-                                    int value = '?';
-                                    try
-                                    {
-                                        value = Integer.parseInt( text.substring( i + 1, i + 1 + octalChars ), 8 );
-                                    }
-                                    catch ( NumberFormatException e )
-                                    {
-                                        if ( getLog().isDebugEnabled() )
-                                        {
-                                            getLog().debug(
-                                                            "Not a number: "
-                                                                + text.substring( i + 1, i + 1 + octalChars ) );
-                                        }
-                                    }
-
-                                    i += octalChars;
-                                    buffer.append( (char) value );
-                                }
-                                else
-                                {
-                                    buffer.append( BACKSLASH );
-                                }
-                        }
-                    }
-                    else
-                    {
-                        buffer.append( BACKSLASH );
-                    }
-                    break;
-
-                case LEFT_CURLY_BRACKET: /*}*/
-                    if ( !anchor && !link )
-                    {
-                        if ( i + 1 < end && text.charAt( i + 1 ) == LEFT_CURLY_BRACKET /*}*/)
-                        {
-                            ++i;
-                            link = true;
-                            flushTraversed( buffer, sink );
-
-                            String linkAnchor = null;
-
-                            if ( i + 1 < end && text.charAt( i + 1 ) == LEFT_CURLY_BRACKET /*}*/)
-                            {
-                                ++i;
-                                StringBuffer buf = new StringBuffer();
-                                i = skipTraversedLinkAnchor( text, i + 1, end, buf );
-                                linkAnchor = buf.toString();
-                            }
-
-                            if ( linkAnchor == null )
-                            {
-                                linkAnchor = getTraversedLink( text, i + 1, end );
-                            }
-
-                            sink.link( linkAnchor );
-                        }
-                        else
-                        {
-                            anchor = true;
-                            flushTraversed( buffer, sink );
-                            sink.anchor( getTraversedAnchor( text, i + 1, end ) );
-                        }
-                    }
-                    else
-                    {
-                        buffer.append( c );
-                    }
-                    break;
-
-                case /*{*/RIGHT_CURLY_BRACKET:
-                    if ( link && i + 1 < end && text.charAt( i + 1 ) == /*{*/RIGHT_CURLY_BRACKET )
-                    {
-                        ++i;
-                        link = false;
-                        flushTraversed( buffer, sink );
-                        sink.link_();
-                    }
-                    else if ( anchor )
-                    {
-                        anchor = false;
-                        flushTraversed( buffer, sink );
-                        sink.anchor_();
-                    }
-                    else
+        }
+
+        blockFileName = source.getName();
+        blockLineNumber = source.getLineNumber();
+        block = null;
+        switch ( line.charAt( i ) )
+        {
+            case STAR:
+                if ( indent == 0 )
+                {
+                    if ( charAt( line, length, i + 1 ) == MINUS && charAt( line, length, i + 2 ) == MINUS )
                     {
-                        buffer.append( c );
+                        block = new Table( indent, line );
                     }
-                    break;
-
-                case LESS_THAN:
-                    if ( !italic && !bold && !monospaced )
+                    else if ( charAt( line, length, i + 1 ) == STAR )
                     {
-                        if ( i + 1 < end && text.charAt( i + 1 ) == LESS_THAN )
+                        if ( charAt( line, length, i + 2 ) == STAR )
                         {
-                            if ( i + 2 < end && text.charAt( i + 2 ) == LESS_THAN )
+                            if ( charAt( line, length, i + 3 ) == STAR )
                             {
-                                i += 2;
-                                monospaced = true;
-                                flushTraversed( buffer, sink );
-                                sink.monospaced();
+                                block = new Section5( indent, line );
                             }
                             else
                             {
-                                ++i;
-                                bold = true;
-                                flushTraversed( buffer, sink );
-                                sink.bold();
+                                block = new Section4( indent, line );
                             }
                         }
                         else
                         {
-                            italic = true;
-                            flushTraversed( buffer, sink );
-                            sink.italic();
+                            block = new Section3( indent, line );
                         }
                     }
                     else
                     {
-                        buffer.append( c );
-                    }
-                    break;
-
-                case GREATER_THAN:
-                    if ( monospaced && i + 2 < end && text.charAt( i + 1 ) == GREATER_THAN
-                        && text.charAt( i + 2 ) == GREATER_THAN )
-                    {
-                        i += 2;
-                        monospaced = false;
-                        flushTraversed( buffer, sink );
-                        sink.monospaced_();
-                    }
-                    else if ( bold && i + 1 < end && text.charAt( i + 1 ) == GREATER_THAN )
-                    {
-                        ++i;
-                        bold = false;
-                        flushTraversed( buffer, sink );
-                        sink.bold_();
+                        block = new Section2( indent, line );
                     }
-                    else if ( italic )
+                }
+                else
+                {
+                    block = new ListItem( indent, line );
+                }
+                break;
+            case LEFT_SQUARE_BRACKET:
+                if ( charAt( line, length, i + 1 ) == RIGHT_SQUARE_BRACKET )
+                {
+                    block = new ListBreak( indent, line );
+                }
+                else
+                {
+                    if ( indent == 0 )
                     {
-                        italic = false;
-                        flushTraversed( buffer, sink );
-                        sink.italic_();
+                        block = new Figure( indent, line );
                     }
                     else
                     {
-                        buffer.append( c );
-                    }
-                    break;
+                        if ( charAt( line, length, i + 1 ) == LEFT_SQUARE_BRACKET )
+                        {
+                            int numbering;
 
-                default:
-                    if ( Character.isWhitespace( c ) )
-                    {
-                        buffer.append( SPACE );
+                            switch ( charAt( line, length, i + 2 ) )
+                            {
+                                case NUMBERING_LOWER_ALPHA_CHAR:
+                                    numbering = Sink.NUMBERING_LOWER_ALPHA;
+                                    break;
+                                case NUMBERING_UPPER_ALPHA_CHAR:
+                                    numbering = Sink.NUMBERING_UPPER_ALPHA;
+                                    break;
+                                case NUMBERING_LOWER_ROMAN_CHAR:
+                                    numbering = Sink.NUMBERING_LOWER_ROMAN;
+                                    break;
+                                case NUMBERING_UPPER_ROMAN_CHAR:
+                                    numbering = Sink.NUMBERING_UPPER_ROMAN;
+                                    break;
+                                case NUMBERING:
+                                default:
+                                    // The first item establishes the numbering
+                                    // scheme for the whole list.
+                                    numbering = Sink.NUMBERING_DECIMAL;
+                            }
 
-                        // Skip to the last char of a sequence of white spaces.
-                        while ( i + 1 < end && Character.isWhitespace( text.charAt( i + 1 ) ) )
+                            block = new NumberedListItem( indent, line, numbering );
+                        }
+                        else
                         {
-                            ++i;
+                            block = new DefinitionListItem( indent, line );
                         }
                     }
+                }
+                break;
+            case MINUS:
+                if ( charAt( line, length, i + 1 ) == MINUS && charAt( line, length, i + 2 ) == MINUS )
+                {
+                    if ( indent == 0 )
+                    {
+                        block = new Verbatim( indent, line );
+                    }
                     else
                     {
-                        buffer.append( c );
+                        if ( firstBlock )
+                        {
+                            block = new Title( indent, line );
+                        }
                     }
-            }
+                }
+                break;
+            case PLUS:
+                if ( indent == 0 && charAt( line, length, i + 1 ) == MINUS && charAt( line, length, i + 2 ) == MINUS )
+                {
+                    block = new Verbatim( indent, line );
+                }
+                break;
+            case EQUAL:
+                if ( indent == 0 && charAt( line, length, i + 1 ) == EQUAL && charAt( line, length, i + 2 ) == EQUAL )
+                {
+                    block = new HorizontalRule( indent, line );
+                }
+                break;
+            case PAGE_BREAK:
+                if ( indent == 0 )
+                {
+                    block = new PageBreak( indent, line );
+                }
+                break;
+            case PERCENT:
+                if ( indent == 0 && charAt( line, length, i + 1 ) == LEFT_CURLY_BRACKET )
+                {
+                    block = new MacroBlock( indent, line );
+                }
+                break;
+            case COMMENT:
+                if ( indent == 0 && charAt( line, length, i + 1 ) == COMMENT )
+                {
+                    block = new Comment( line.substring( 2 ).trim() );
+                }
+                break;
+            default:
+                break;
         }
 
-        if ( monospaced )
-        {
-            throw new AptParseException( "missing '" + MONOSPACED_END_MARKUP + "'" );
-        }
-        if ( bold )
-        {
-            throw new AptParseException( "missing '" + BOLD_END_MARKUP + "'" );
-        }
-        if ( italic )
-        {
-            throw new AptParseException( "missing '" + ITALIC_END_MARKUP + "'" );
-        }
-        if ( link )
+        if ( block == null )
         {
-            throw new AptParseException( "missing '" + LINK_END_MARKUP + "'" );
+            if ( indent == 0 )
+            {
+                block = new Section1( indent, line );
+            }
+            else
+            {
+                block = new Paragraph( indent, line );
+            }
         }
-        if ( anchor )
+    }
+
+    /**
+     * Checks that the current block is of the expected type.
+     *
+     * @param type the expected type.
+     * @throws AptParseException if something goes wrong.
+     */
+    private void expectedBlock( int type )
+        throws AptParseException
+    {
+        int blockType = block.getType();
+
+        if ( blockType != type )
         {
-            throw new AptParseException( "missing '" + ANCHOR_END_MARKUP + "'" );
+            throw new AptParseException( "expected " + TYPE_NAMES[type] + ", found " + TYPE_NAMES[blockType] );
         }
+    }
 
-        flushTraversed( buffer, sink );
+    // -----------------------------------------------------------------------
+
+    /**
+     * Determine if c is an octal character.
+     *
+     * @param c the character.
+     * @return boolean
+     */
+    private static boolean isOctalChar( char c )
+    {
+        return ( c >= '0' && c <= '7' );
+    }
+
+    /**
+     * Determine if c is an hex character.
+     *
+     * @param c the character.
+     * @return boolean
+     */
+    private static boolean isHexChar( char c )
+    {
+        return ( ( c >= '0' && c <= '9' ) || ( c >= 'a' && c <= 'f' ) || ( c >= 'A' && c <= 'F' ) );
     }
 
     /**
@@ -1526,16 +1564,19 @@
 
         Sink linkSink = new SinkAdapter()
         {
+            /** {@inheritDoc} */
             public void lineBreak()
             {
                 buffer.append( SPACE );
             }
 
+            /** {@inheritDoc} */
             public void nonBreakingSpace()
             {
                 buffer.append( SPACE );
             }
 
+            /** {@inheritDoc} */
             public void text( String text )
             {
                 buffer.append( text );
@@ -2828,39 +2869,5 @@
 
             return result;
         }
-    }
-
-    // -----------------------------------------------------------------------
-
-    /**
-     * Replace part of a string.
-     *
-     * @param string the string
-     * @param oldSub the substring to replace
-     * @param newSub the replacement string
-     * @return String
-     */
-    private static String replaceAll( String string, String oldSub, String newSub )
-    {
-        StringBuffer replaced = new StringBuffer();
-        int oldSubLength = oldSub.length();
-        int begin, end;
-
-        begin = 0;
-        while ( ( end = string.indexOf( oldSub, begin ) ) >= 0 )
-        {
-            if ( end > begin )
-            {
-                replaced.append( string.substring( begin, end ) );
-            }
-            replaced.append( newSub );
-            begin = end + oldSubLength;
-        }
-        if ( begin < string.length() )
-        {
-            replaced.append( string.substring( begin ) );
-        }
-
-        return replaced.toString();
     }
 }