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