001/*
002    Licensed to the Apache Software Foundation (ASF) under one
003    or more contributor license agreements.  See the NOTICE file
004    distributed with this work for additional information
005    regarding copyright ownership.  The ASF licenses this file
006    to you under the Apache License, Version 2.0 (the
007    "License"); you may not use this file except in compliance
008    with the License.  You may obtain a copy of the License at
009
010       http://www.apache.org/licenses/LICENSE-2.0
011
012    Unless required by applicable law or agreed to in writing,
013    software distributed under the License is distributed on an
014    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015    KIND, either express or implied.  See the License for the
016    specific language governing permissions and limitations
017    under the License.
018 */
019package org.apache.wiki.render;
020
021import org.apache.wiki.api.core.Context;
022import org.apache.wiki.parser.PluginContent;
023import org.apache.wiki.parser.WikiDocument;
024import org.jdom2.Content;
025import org.jdom2.Element;
026import org.jdom2.Text;
027
028import java.util.ArrayList;
029import java.util.List;
030import java.util.stream.Collectors;
031import java.util.stream.IntStream;
032
033/**
034 *  Implements DOM-to-Creole rendering.
035 *  <p>
036 *  FIXME: This class is not yet completely done.
037 *
038 */
039public class CreoleRenderer extends WikiRenderer {
040
041    private static final String IMG_START = "{{";
042    private static final String IMG_END = "}}";
043    private static final String PLUGIN_START = "<<";
044    private static final String PLUGIN_END = ">>";
045    private static final String HREF_START = "[[";
046    private static final String HREF_DELIMITER = "|";
047    private static final String HREF_END = "]]";
048    private static final String PRE_START = "{{{";
049    private static final String PRE_END = "}}}";
050    private static final String PLUGIN_IMAGE = "Image";
051    private static final String PARAM_SRC = "src";
052    private static final String HREF_ATTRIBUTE = "href";
053    private static final String ONE_SPACE = " ";
054    private static final String EMPTY_STRING = "";
055    private static final String LINEBREAK = "\n";
056    private static final String LI = "li";
057    private static final String UL = "ul";
058    private static final String OL = "ol";
059    private static final String P  = "p";
060    private static final String A  = "a";
061    private static final String PRE = "pre";
062
063    /**
064     * Contains element, start markup, end markup
065     */
066    private static final String[] ELEMENTS = {
067       "i" , "//"    , "//",
068       "b" , "**"    , "**",
069       "h2", "== "   , " ==",
070       "h3", "=== "  , " ===",
071       "h4", "==== " , " ====",
072       "hr", "----"  , EMPTY_STRING,
073       "tt", "<<{{>>", "<<}}>>"
074    };
075
076    private int m_listCount;
077    private char m_listChar = 'x';
078
079    private final List< PluginContent > m_plugins = new ArrayList<>();
080
081    /**
082     *  Creates a new Creole Renderer.
083     */
084    public CreoleRenderer( final Context ctx, final WikiDocument doc )
085    {
086        super( ctx, doc );
087    }
088
089    /**
090     * Renders an element into the StringBuilder given
091     * @param ce element to render
092     * @param sb stringbuilder holding the element render
093     */
094    private void renderElement( final Element ce, final StringBuilder sb ) {
095        String endEl = EMPTY_STRING;
096        for( int i = 0; i < ELEMENTS.length; i+=3 ) {
097            if( ELEMENTS[i].equals(ce.getName()) ) {
098                sb.append( ELEMENTS[i+1] );
099                endEl = ELEMENTS[i+2];
100            }
101        }
102
103        if( UL.equals(ce.getName()) ) {
104            m_listCount++;
105            m_listChar = '*';
106        } else if( OL.equals(ce.getName()) ) {
107            m_listCount++;
108            m_listChar = '#';
109        } else if( LI.equals(ce.getName()) ) {
110            sb.append(IntStream.range(0, m_listCount).mapToObj(i -> String.valueOf(m_listChar)).collect(Collectors.joining("", "", ONE_SPACE)));
111        } else if( A.equals( ce.getName() ) ) {
112            final String href = ce.getAttributeValue( HREF_ATTRIBUTE );
113            final String text = ce.getText();
114
115            if( href.equals( text ) ) {
116                sb.append( HREF_START ).append( href ).append( HREF_END );
117            } else {
118                sb.append( HREF_START ).append( href ).append( HREF_DELIMITER ).append( text ).append( HREF_END);
119            }
120            // Do not render anything else
121            return;
122        } else if( PRE.equals( ce.getName() ) ) {
123            sb.append( PRE_START );
124            sb.append( ce.getText() );
125            sb.append( PRE_END );
126
127            return;
128        }
129
130        //  Go through the children
131        for( final Content c : ce.getContent() ) {
132            if( c instanceof PluginContent ) {
133                final PluginContent pc = ( PluginContent )c;
134
135                if( pc.getPluginName().equals( PLUGIN_IMAGE ) ) {
136                    sb.append( IMG_START ).append( pc.getParameter( PARAM_SRC ) ).append( IMG_END );
137                } else {
138                    m_plugins.add(pc);
139                    sb.append( PLUGIN_START ).append( pc.getPluginName() ).append( ONE_SPACE ).append( m_plugins.size() ).append( PLUGIN_END );
140                }
141            } else if( c instanceof Text ) {
142                sb.append( ( ( Text )c ).getText() );
143            } else if( c instanceof Element ) {
144                renderElement( ( Element )c, sb );
145            }
146        }
147
148        if( UL.equals( ce.getName() ) || OL.equals( ce.getName() ) ) {
149            m_listCount--;
150        } else if( P.equals( ce.getName() ) ) {
151            sb.append( LINEBREAK );
152        }
153
154        sb.append(endEl);
155    }
156
157    /**
158     *  {@inheritDoc}
159     */
160    @Override
161    public String getString() {
162        final StringBuilder sb = new StringBuilder(1000);
163        final Element ce = m_document.getRootElement();
164
165        //  Traverse through the entire tree of everything.
166        renderElement( ce, sb );
167        return sb.toString();
168    }
169
170}