001/*
002 *  Copyright 2017 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.plugins.odfsync.cdmfr.transformers;
017
018import java.io.IOException;
019import java.util.Map;
020
021import org.apache.avalon.framework.parameters.Parameters;
022import org.apache.cocoon.ProcessingException;
023import org.apache.cocoon.environment.SourceResolver;
024import org.apache.cocoon.transformation.AbstractTransformer;
025import org.apache.commons.lang.ArrayUtils;
026import org.apache.commons.lang.StringUtils;
027import org.xml.sax.Attributes;
028import org.xml.sax.SAXException;
029import org.xml.sax.helpers.AttributesImpl;
030
031/**
032 * This transformer provides a valid docbook output for rich-text issued from CDM-fr:
033 * <ul>
034 *      <li>- no character (break line, bold, text, ...) outside a <code>para</code> element</li>
035 *      <li>- no <code>title</code> outside a <code>section</code> element</li>
036 * </ul>
037 */
038public class CDMRichTextTransformer extends AbstractTransformer
039{
040    private static final String __TAG_ARTICLE = "article";
041    private static final String __TAG_SECTION = "section";
042    private static final String __TAG_TITLE = "title";
043    
044    private static final String[] __CHARACTER_TYPES = new String[] {"emphasis", "phrase", "link"};
045    private static final String[] __BLOC_TYPES = new String[] {"itemizedlist", "para", "orderedlist", "table"};
046    
047    int _sectionCount;
048    int _titleCount;
049    
050    boolean _inDocbook;
051    boolean _inBlock;
052    boolean _inSection;
053    boolean _inTitle;
054    
055    boolean _startPara;
056    
057    private void _init ()
058    {
059        _sectionCount = 0;
060        _titleCount = 0;
061        
062        _inDocbook = true;
063        _inBlock = false;
064        _inSection = false;
065        _inTitle = false;
066        
067        _startPara = false;
068    }
069    
070    @Override
071    public void startElement(String uri, String localName, String raw, Attributes attrs) throws SAXException
072    {
073        if (__TAG_ARTICLE.equals(localName))
074        {
075            _init();
076        }
077        
078        if (_inDocbook && __TAG_SECTION.equals(localName))
079        {
080            _sectionCount++;
081            
082            if (_startPara)
083            {
084                // End <para>
085                _endParaElement();
086            }
087        }
088        
089        if (_inDocbook && __TAG_TITLE.equals(localName))
090        {
091            _titleCount++;
092            _inTitle = true;
093            
094            if (_titleCount > _sectionCount)
095            {
096                if (_startPara)
097                {
098                    // End <para>
099                    _endParaElement();
100                }
101                
102                _sectionCount++;
103                super.startElement("", "section", "section", new AttributesImpl());
104            }
105        }
106        
107        if (ArrayUtils.contains(__BLOC_TYPES, localName))
108        {
109            if (_startPara)
110            {
111                // End <para>
112                _endParaElement();
113            }
114            _inBlock = true;
115        }
116        
117        // Find <emphasis>, <phrase> or <link> without outside a <listItem> or <para> parent
118        if (ArrayUtils.contains(__CHARACTER_TYPES, localName) && !_inBlock)
119        {
120            // Insert <para> element
121            _startParaElement();
122        }
123        
124        super.startElement(uri, localName, raw, attrs);
125    }
126    
127    @Override
128    public void endElement(String uri, String localName, String raw) throws SAXException
129    {
130        if (__TAG_ARTICLE.equals(localName))
131        {
132            _inDocbook = false;
133            
134            if (_startPara)
135            {
136                // End <para>
137                _endParaElement();
138            }
139            
140            while (_sectionCount != 0)
141            {
142                contentHandler.endElement("", "section", "section");
143                _sectionCount--;
144                _titleCount--;
145            }
146        }
147        
148        if (__TAG_TITLE.equals(localName))
149        {
150            _inTitle = false;
151        }
152        
153        if (__TAG_SECTION.equals(localName))
154        {
155            _sectionCount--;
156            _titleCount--;
157            
158            if (_startPara)
159            {
160                // End <para>
161                _endParaElement();
162            }
163        }
164        
165        if (_inDocbook && ArrayUtils.contains(__BLOC_TYPES, localName))
166        {
167            _inBlock = false;
168        }
169        
170        super.endElement(uri, localName, raw);
171    }
172    
173    @Override
174    public void characters(char[] c, int start, int len) throws SAXException
175    {
176        boolean isBlank = StringUtils.isBlank(new String (c, start, len));
177        
178        if (!isBlank && _inDocbook && !_inBlock && !_inTitle)
179        {
180            _startParaElement();
181        }
182        super.characters(c, start, len);
183    }
184    
185    private void _startParaElement () throws SAXException
186    {
187        _startPara = true;
188        _inBlock = true;
189        contentHandler.startElement("", "para", "para", new AttributesImpl());
190    }
191    
192    private void _endParaElement () throws SAXException
193    {
194        _startPara = false;
195        _inBlock = false;
196        contentHandler.endElement("", "para", "para");
197    }
198
199    @Override
200    public void setup(SourceResolver resolver, Map objectModel, String src, Parameters par) throws ProcessingException, SAXException, IOException
201    {
202        // Nothing
203    }
204}