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    /** {@inheritDoc} */
135    public final synchronized void parse( final Reader source, final Sink sink )
136        throws ParseException
137    {
138        init();
139
140        List<Block> blocks;
141        final ByLineSource src = new ByLineReaderSource( source );
142
143        try
144        {
145            blocks = parse( src );
146        }
147        catch ( final Exception e )
148        {
149            // TODO handle column number
150            throw new ParseException( e, src.getName(), src.getLineNumber(), -1 );
151        }
152
153        sink.head();
154
155        final String title = getTitle( blocks, src );
156        if ( title != null )
157        {
158            sink.title();
159            sink.text( title );
160            sink.title_();
161        }
162
163        sink.head_();
164        sink.body();
165        for ( Block block : blocks )
166        {
167            block.traverse( sink );
168        }
169        sink.body_();
170        sink.flush();
171        sink.close();
172
173        setSecondParsing( false );
174        init();
175    }
176
177    /**
178     * Guess a title for the page. It uses the first section that it finds.
179     * If it doesn't find any section tries to get it from
180     * {@link ByLineReaderSource#getName()}
181     *
182     * @param blocks blocks to parse
183     * @param source source to parse
184     * @return a title for a page
185     * @since 1.1
186     */
187    public final String getTitle( final List<Block> blocks, final ByLineSource source )
188    {
189        String title = null;
190
191        for ( Block block : blocks )
192        {
193            if ( block instanceof SectionBlock )
194            {
195                final SectionBlock sectionBlock = (SectionBlock) block;
196                title = sectionBlock.getTitle();
197                break;
198            }
199        }
200
201        if ( title == null )
202        {
203            String name = source.getName();
204            if ( name != null )
205            {
206                name = name.trim();
207
208                if ( name.length() != 0 )
209                {
210                    if ( name.endsWith( ".twiki" ) )
211                    {
212                        title = name.substring( 0, name.length() - EXTENSION_LENGTH );
213                    }
214                    else
215                    {
216                        title = name;
217                    }
218                }
219            }
220        }
221
222        return title;
223    }
224
225    /** {@inheritDoc} */
226    protected void init()
227    {
228        super.init();
229
230        paraParser.setSectionParser( sectionParser );
231        paraParser.setListParser( listParser );
232        paraParser.setTextParser( formatTextParser );
233        paraParser.setHrulerParser( hrulerParser );
234        paraParser.setTableBlockParser( tableParser );
235        paraParser.setVerbatimParser( verbatimParser );
236        sectionParser.setParaParser( paraParser );
237        sectionParser.setHrulerParser( hrulerParser );
238        sectionParser.setVerbatimBlockParser( verbatimParser );
239        listParser.setTextParser( formatTextParser );
240        formatTextParser.setTextParser( textParser );
241        tableParser.setTextParser( formatTextParser );
242
243        this.parsers = new BlockParser[] { sectionParser, hrulerParser, verbatimParser, paraParser };
244    }
245}