001/*
002 *  Copyright 2011 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.glossary;
017
018import java.io.IOException;
019import java.text.Normalizer;
020import java.text.Normalizer.Form;
021import java.util.Arrays;
022import java.util.Collections;
023import java.util.HashMap;
024import java.util.List;
025import java.util.Map;
026
027import org.apache.avalon.framework.service.ServiceException;
028import org.apache.avalon.framework.service.ServiceManager;
029import org.apache.cocoon.ProcessingException;
030import org.apache.cocoon.environment.ObjectModelHelper;
031import org.apache.cocoon.environment.Request;
032import org.apache.cocoon.generation.ServiceableGenerator;
033import org.apache.cocoon.xml.AttributesImpl;
034import org.apache.cocoon.xml.XMLUtils;
035import org.apache.commons.lang.StringUtils;
036import org.xml.sax.SAXException;
037
038import org.ametys.plugins.glossary.theme.ThemeExpression;
039import org.ametys.plugins.glossary.theme.ThemesDAO;
040import org.ametys.plugins.repository.AmetysObjectIterable;
041import org.ametys.plugins.repository.AmetysObjectResolver;
042import org.ametys.plugins.repository.data.holder.ModelAwareDataHolder;
043import org.ametys.plugins.repository.query.expression.Expression;
044import org.ametys.plugins.repository.query.expression.OrExpression;
045import org.ametys.web.repository.page.Page;
046import org.ametys.web.repository.page.ZoneItem;
047import org.ametys.web.repository.site.SiteManager;
048
049/**
050 * Generate the list of word definitions as XML.
051 */
052public class DefinitionsGenerator extends ServiceableGenerator
053{
054    private SiteManager _siteManager;
055    private ThemesDAO _themesDAO;
056    private AmetysObjectResolver _ametysObjectResolver;
057    
058    @Override
059    public void service(ServiceManager serviceManager) throws ServiceException
060    {
061        super.service(serviceManager);
062        _siteManager = (SiteManager) serviceManager.lookup(SiteManager.ROLE);
063        _themesDAO = (ThemesDAO) serviceManager.lookup(ThemesDAO.ROLE);
064        _ametysObjectResolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
065    }
066    
067    @Override
068    public void generate() throws IOException, SAXException, ProcessingException
069    {
070        Request request = ObjectModelHelper.getRequest(objectModel);
071        String siteName = parameters.getParameter("siteName", (String) request.getAttribute("site"));
072        String language = parameters.getParameter("language", (String) request.getAttribute("sitemapLanguage"));
073        String letter = parameters.getParameter("letter", request.getParameter("letter"));
074        letter = StringUtils.isBlank(letter) ? "A" : letter.toUpperCase();
075        Page page = (Page) request.getAttribute(Page.class.getName());
076        ZoneItem zoneItem = (ZoneItem) request.getAttribute(ZoneItem.class.getName());
077        ModelAwareDataHolder serviceParameters = zoneItem.getServiceParameters();
078        String[] themes = serviceParameters.getValue("themes", false, new String[0]);
079        
080        contentHandler.startDocument();
081        
082        AttributesImpl attrs = new AttributesImpl();
083        attrs.addCDATAAttribute("siteName", siteName);
084        attrs.addCDATAAttribute("language", language);
085        attrs.addCDATAAttribute("letter", letter);
086        if (page != null)
087        {
088            attrs.addCDATAAttribute("page", page.getId());
089        }
090        XMLUtils.startElement(contentHandler, "wordDefinitions", attrs);
091        
092        Expression themeExpression = _getThemeExpression(siteName, language, themes);
093        
094        String definitionQuery = GlossaryHelper.getDefinitionQuery(siteName, language, themeExpression);
095        try (AmetysObjectIterable<DefaultDefinition> definitions = _ametysObjectResolver.query(definitionQuery);)
096        {
097            for (DefaultDefinition definition : definitions)
098            {
099                saxDefinition(definition);
100            }
101        }
102        
103        XMLUtils.endElement(contentHandler, "wordDefinitions");
104        contentHandler.endDocument();
105    }
106
107    /**
108     * Get the list of theme expressions to filter the definitions with
109     * @param siteName the site's name
110     * @param language the site's language
111     * @param themes the array of theme Ids to create expressions
112     * @return a list of ThemeExpression
113     */
114    protected Expression _getThemeExpression(String siteName, String language, String[] themes)
115    {
116        ThemeExpression[] themeExpressions = Arrays.stream(themes)
117            .filter(StringUtils::isNotBlank)
118            .filter(id -> themeExists(id, siteName, language))
119            .map(id -> new ThemeExpression(id))
120            .toArray(ThemeExpression[]::new);
121        
122        return themeExpressions.length == 0 ? null : new OrExpression(themeExpressions);
123    }
124    
125    /**
126     * Generate a word definition.
127     * @param definition the definition to generate.
128     * @throws SAXException If an error occurs while generating the XML.
129     */
130    protected void saxDefinition(DefaultDefinition definition) throws SAXException
131    {
132        AttributesImpl attrs = new AttributesImpl();
133        attrs.addCDATAAttribute("id", definition.getId());
134        
135        String word = definition.getWord();
136        if (StringUtils.isNotEmpty(word))
137        {
138            String firstLetter = Normalizer.normalize(word.substring(0, 1), Form.NFD).replaceAll("\\p{InCombiningDiacriticalMarks}+", "");
139            attrs.addCDATAAttribute("firstLetter", firstLetter.toUpperCase());
140        }
141        attrs.addCDATAAttribute("word", definition.getWord());
142        attrs.addCDATAAttribute("variantsText", StringUtils.join(definition.getVariants(), ", "));
143        attrs.addCDATAAttribute("displayOnText", Boolean.toString(definition.displayOnText()));
144        
145        XMLUtils.startElement(contentHandler, "wordDefinition", attrs);
146        
147        // Variants.
148        XMLUtils.startElement(contentHandler, "variants");
149        
150        for (String variant : definition.getVariants())
151        {
152            AttributesImpl variantAttrs = new AttributesImpl();
153            variantAttrs.addCDATAAttribute("word", variant);
154            XMLUtils.createElement(contentHandler, "variant", variantAttrs);
155        }
156        
157        XMLUtils.endElement(contentHandler, "variants");
158        
159        // Content
160        XMLUtils.createElement(contentHandler, "content", definition.getContent());
161        
162        XMLUtils.endElement(contentHandler, "wordDefinition");
163    }
164    
165    /**
166     * Verify the existence of a theme
167     * @param themeName the id of the theme to verify
168     * @param siteName the site's name
169     * @param language the site's language
170     * @return true if the theme exists, false otherwise
171     */
172    public boolean themeExists(String themeName, String siteName, String language)
173    {
174        if (StringUtils.isBlank(themeName))
175        {
176            return false;
177        }
178        Map<String, Object> contextualParameters = new HashMap<>();
179        contextualParameters.put("language", language);
180        contextualParameters.put("siteName", siteName);
181        List<String> checkTags = _themesDAO.checkTags(List.of(themeName), false, Collections.EMPTY_MAP, contextualParameters);
182        return !checkTags.isEmpty();
183    }
184}