001package org.apache.maven.doxia.module.twiki;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import org.apache.maven.doxia.module.twiki.parser.Block;
023import org.apache.maven.doxia.module.twiki.parser.BlockParser;
024import org.apache.maven.doxia.module.twiki.parser.FormatedTextParser;
025import org.apache.maven.doxia.module.twiki.parser.GenericListBlockParser;
026import org.apache.maven.doxia.module.twiki.parser.HRuleBlockParser;
027import org.apache.maven.doxia.module.twiki.parser.ParagraphBlockParser;
028import org.apache.maven.doxia.module.twiki.parser.SectionBlock;
029import org.apache.maven.doxia.module.twiki.parser.SectionBlockParser;
030import org.apache.maven.doxia.module.twiki.parser.TableBlockParser;
031import org.apache.maven.doxia.module.twiki.parser.TextParser;
032import org.apache.maven.doxia.module.twiki.parser.VerbatimBlockParser;
033import org.apache.maven.doxia.module.twiki.parser.XHTMLWikiWordLinkResolver;
034import org.apache.maven.doxia.parser.AbstractTextParser;
035import org.apache.maven.doxia.parser.ParseException;
036import org.apache.maven.doxia.parser.Parser;
037import org.apache.maven.doxia.sink.Sink;
038import org.apache.maven.doxia.util.ByLineReaderSource;
039import org.apache.maven.doxia.util.ByLineSource;
040import org.codehaus.plexus.component.annotations.Component;
041
042import java.io.Reader;
043import java.util.ArrayList;
044import java.util.List;
045
046/**
047 * Parse the <a href="http://twiki.org/cgi-bin/view/TWiki/TextFormattingRules">
048 * twiki file format</a>
049 *
050 * @author Juan F. Codagnone
051 * @version $Id$
052 * @since 1.0
053 */
054@Component( role = Parser.class, hint = "twiki" )
055public class TWikiParser
056    extends AbstractTextParser
057{
058    private static final int EXTENSION_LENGTH = 6;
059
060    /** paragraph parser. */
061    private final ParagraphBlockParser paraParser = new ParagraphBlockParser();
062
063    /** section parser. */
064    private final SectionBlockParser sectionParser = new SectionBlockParser();
065
066    /** enumeration parser. */
067    private final GenericListBlockParser listParser = new GenericListBlockParser();
068
069    /** Text parser. */
070    private final FormatedTextParser formatTextParser = new FormatedTextParser();
071
072    /**
073     * text parser.
074     * This only works for xhtml output, but there is no way
075     * of transforming a wikiWord in another context.
076     */
077    private final TextParser textParser = new TextParser( new XHTMLWikiWordLinkResolver() );
078
079    /** hruler parser. */
080    private final HRuleBlockParser hrulerParser = new HRuleBlockParser();
081
082    /** table parser. */
083    private final TableBlockParser tableParser = new TableBlockParser();
084
085    /** verbatim parser. */
086    private final VerbatimBlockParser verbatimParser = new VerbatimBlockParser();
087
088    /** list of parsers to try to apply to the toplevel */
089    private BlockParser[] parsers;
090
091    /**
092     * Creates the TWikiParser.
093     */
094    public TWikiParser()
095    {
096        init();
097    }
098
099    /**
100     * <p>parse.</p>
101     *
102     * @param source source to parse.
103     * @return the blocks that represent source.
104     * @throws org.apache.maven.doxia.parser.ParseException on error.
105     */
106    public final List<Block> parse( final ByLineSource source )
107        throws ParseException
108    {
109        final List<Block> ret = new ArrayList<Block>();
110
111        String line;
112        while ( ( line = source.getNextLine() ) != null )
113        {
114            boolean accepted = false;
115
116            for ( BlockParser parser : parsers )
117            {
118                if ( parser.accept( line ) )
119                {
120                    accepted = true;
121                    ret.add( parser.visit( line, source ) );
122                    break;
123                }
124            }
125            if ( !accepted )
126            {
127                throw new ParseException( "Line number not handle : " + source.getLineNumber() + ": " + line );
128            }
129        }
130
131        return ret;
132    }
133
134    @Override
135    public void parse( Reader source, Sink sink )
136        throws ParseException
137    {
138        parse( source, sink, "" );
139    }
140    
141    @Override
142    public final synchronized void parse( final Reader source, final Sink sink, String reference )
143        throws ParseException
144    {
145        init();
146
147        List<Block> blocks;
148        final ByLineSource src = new ByLineReaderSource( source, reference );
149
150        try
151        {
152            blocks = parse( src );
153        }
154        catch ( final Exception e )
155        {
156            // TODO handle column number
157            throw new ParseException( e, src.getName(), src.getLineNumber(), -1 );
158        }
159
160        sink.head();
161
162        final String title = getTitle( blocks, src );
163        if ( title != null )
164        {
165            sink.title();
166            sink.text( title );
167            sink.title_();
168        }
169
170        sink.head_();
171        sink.body();
172        for ( Block block : blocks )
173        {
174            block.traverse( sink );
175        }
176        sink.body_();
177        sink.flush();
178        sink.close();
179
180        setSecondParsing( false );
181        init();
182    }
183
184    /**
185     * Guess a title for the page. It uses the first section that it finds.
186     * If it doesn't find any section tries to get it from
187     * {@link ByLineReaderSource#getName()}
188     *
189     * @param blocks blocks to parse
190     * @param source source to parse
191     * @return a title for a page
192     * @since 1.1
193     */
194    public final String getTitle( final List<Block> blocks, final ByLineSource source )
195    {
196        String title = null;
197
198        for ( Block block : blocks )
199        {
200            if ( block instanceof SectionBlock )
201            {
202                final SectionBlock sectionBlock = (SectionBlock) block;
203                title = sectionBlock.getTitle();
204                break;
205            }
206        }
207
208        if ( title == null )
209        {
210            String name = source.getName();
211            if ( name != null )
212            {
213                name = name.trim();
214
215                if ( name.length() != 0 )
216                {
217                    if ( name.endsWith( ".twiki" ) )
218                    {
219                        title = name.substring( 0, name.length() - EXTENSION_LENGTH );
220                    }
221                    else
222                    {
223                        title = name;
224                    }
225                }
226            }
227        }
228
229        return title;
230    }
231
232    /** {@inheritDoc} */
233    protected void init()
234    {
235        super.init();
236
237        paraParser.setSectionParser( sectionParser );
238        paraParser.setListParser( listParser );
239        paraParser.setTextParser( formatTextParser );
240        paraParser.setHrulerParser( hrulerParser );
241        paraParser.setTableBlockParser( tableParser );
242        paraParser.setVerbatimParser( verbatimParser );
243        sectionParser.setParaParser( paraParser );
244        sectionParser.setHrulerParser( hrulerParser );
245        sectionParser.setVerbatimBlockParser( verbatimParser );
246        listParser.setTextParser( formatTextParser );
247        formatTextParser.setTextParser( textParser );
248        tableParser.setTextParser( formatTextParser );
249
250        this.parsers = new BlockParser[] { sectionParser, hrulerParser, verbatimParser, paraParser };
251    }
252}