001/*
002 *  Copyright 2013 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.cms.search.cocoon;
017
018import java.io.IOException;
019import java.util.Collection;
020import java.util.Collections;
021import java.util.Date;
022import java.util.Map;
023
024import org.apache.avalon.framework.service.ServiceException;
025import org.apache.avalon.framework.service.ServiceManager;
026import org.apache.cocoon.ProcessingException;
027import org.apache.cocoon.environment.ObjectModelHelper;
028import org.apache.cocoon.environment.Request;
029import org.apache.cocoon.generation.ServiceableGenerator;
030import org.apache.cocoon.xml.AttributesImpl;
031import org.apache.cocoon.xml.XMLUtils;
032import org.apache.commons.lang3.StringUtils;
033import org.xml.sax.SAXException;
034
035import org.ametys.cms.contenttype.ContentTypesHelper;
036import org.ametys.cms.contenttype.MetadataType;
037import org.ametys.cms.repository.Content;
038import org.ametys.cms.search.SearchResult;
039import org.ametys.cms.search.SearchResults;
040import org.ametys.cms.search.content.ContentValuesExtractorFactory;
041import org.ametys.cms.search.content.ContentValuesExtractorFactory.SearchModelContentValuesExtractor;
042import org.ametys.cms.search.model.ResultField;
043import org.ametys.cms.search.ui.model.SearchUIColumn;
044import org.ametys.cms.search.ui.model.SearchUIModel;
045import org.ametys.core.user.User;
046import org.ametys.core.user.UserManager;
047import org.ametys.core.util.ServerCommHelper;
048import org.ametys.plugins.repository.AmetysObjectResolver;
049import org.ametys.plugins.repository.AmetysRepositoryException;
050import org.ametys.runtime.i18n.I18nizableText;
051import org.ametys.runtime.parameter.Enumerator;
052import org.ametys.runtime.parameter.ParameterHelper;
053
054/**
055 * Generate contents returned by the {@link SearchAction}.
056 */
057public class SearchGenerator extends ServiceableGenerator
058{
059    
060    /** The {@link AmetysObjectResolver} */
061    protected AmetysObjectResolver _resolver;
062    /** The user manager. */
063    protected UserManager _userManager;
064    /** Helper for content types */
065    protected ContentTypesHelper _contentTypesHelper;
066    /** The server comm helper */
067    protected ServerCommHelper _serverCommHelper;
068    /** The content values extractor factory. */
069    protected ContentValuesExtractorFactory _valuesExtractorFactory;
070    
071    @Override
072    public void service(ServiceManager smanager) throws ServiceException
073    {
074        super.service(smanager);
075        _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE);
076        _userManager = (UserManager) smanager.lookup(UserManager.ROLE);
077        _contentTypesHelper = (ContentTypesHelper) smanager.lookup(ContentTypesHelper.ROLE);
078        _serverCommHelper = (ServerCommHelper) smanager.lookup(ServerCommHelper.ROLE);
079        _valuesExtractorFactory = (ContentValuesExtractorFactory) smanager.lookup(ContentValuesExtractorFactory.ROLE);
080    }
081    
082    @SuppressWarnings("unchecked")
083    @Override
084    public void generate() throws IOException, SAXException, ProcessingException
085    {
086        Request request = ObjectModelHelper.getRequest(objectModel);
087        
088        SearchResults<Content> results = (SearchResults<Content>) request.getAttribute(SearchAction.SEARCH_RESULTS);
089        SearchUIModel model = (SearchUIModel) request.getAttribute(SearchAction.SEARCH_MODEL);
090        
091        Map<String, Object> jsParameters = _serverCommHelper.getJsParameters();
092        
093        Map<String, Object> contextualParameters = (Map<String, Object>) jsParameters.get("contextualParameters");
094        if (contextualParameters == null)
095        {
096            contextualParameters = Collections.emptyMap();
097        }
098        
099        boolean generateLabels = parameters.getParameterAsBoolean("generateLabels", false);
100        String language = parameters.getParameter("language", (String) request.getAttribute("sitemapLanguage"));
101        
102        boolean fullValues = parameters.getParameterAsBoolean("fullValues", "true".equals(contextualParameters.get("fullValues")));
103        
104        SearchModelContentValuesExtractor extractor = _valuesExtractorFactory.create(model).setFullValues(fullValues);
105        
106        Collection<SearchUIColumn> resultFields = model.getResultFields(contextualParameters).values();
107        
108        contentHandler.startDocument();
109        XMLUtils.startElement(contentHandler, "contents");
110        
111        Iterable<SearchResult<Content>> contents = results.getResults();
112        for (SearchResult<Content> result : contents)
113        {
114            Content content = result.getObject();
115            Map<String, Object> values = extractor.getValues(content);
116            saxContent(content, resultFields, values, generateLabels, language);
117        }
118        
119        XMLUtils.endElement(contentHandler, "contents");
120        contentHandler.endDocument();
121    }
122    
123    /**
124     * Generate the content's values.
125     * @param content The content.
126     * @param fields The model result fields.
127     * @param values The content values, extracted from the fields.
128     * @param generateLabels true to generate value labels, false to generate raw values.
129     * @param language The language.
130     * @throws SAXException If an error occurs generating the XML.
131     */
132    protected void saxContent(Content content, Collection<SearchUIColumn> fields, Map<String, Object> values, boolean generateLabels, String language) throws SAXException
133    {
134        AttributesImpl attrs = new AttributesImpl();
135        
136        attrs.addCDATAAttribute("id", content.getId());
137        attrs.addCDATAAttribute("name", content.getName());
138        attrs.addCDATAAttribute("title", content.getTitle());
139        attrs.addCDATAAttribute("language", content.getLanguage());
140        String iconGlyph = _contentTypesHelper.getIconGlyph(content);
141        if (iconGlyph != null)
142        {
143            attrs.addCDATAAttribute("iconGlyph", iconGlyph);
144        }
145        String iconDecorator = _contentTypesHelper.getIconDecorator(content);
146        if (iconDecorator != null)
147        {
148            attrs.addCDATAAttribute("iconDecorator", iconDecorator);
149        }
150        attrs.addCDATAAttribute("smallIcon", _contentTypesHelper.getSmallIcon(content));
151        attrs.addCDATAAttribute("mediumIcon", _contentTypesHelper.getMediumIcon(content));
152        attrs.addCDATAAttribute("largeIcon", _contentTypesHelper.getLargeIcon(content));
153        
154        XMLUtils.startElement(contentHandler, "content", attrs);
155        
156        XMLUtils.startElement(contentHandler, "values");
157        
158        for (ResultField resultField : fields)
159        {
160            Object value = values.get(resultField.getId());
161            
162            if (generateLabels)
163            {
164                saxLabel(resultField, value, language);
165            }
166            else
167            {
168                saxValue(resultField, value);
169            }
170        }
171        XMLUtils.endElement(contentHandler, "values");
172        
173        XMLUtils.endElement(contentHandler, "content");
174    }
175    
176    /**
177     * Generate the value of a result field for a content.
178     * @param resultField The result field.
179     * @param value The content value.
180     * @throws SAXException If an error occurs generating the XML.
181     */
182    protected void saxValue(ResultField resultField, Object value) throws SAXException
183    {
184        String fieldName = resultField.getId();
185        
186        if (value != null)
187        {
188            if (value instanceof I18nizableText)
189            {
190                ((I18nizableText) value).toSAX(contentHandler, fieldName);
191            }
192            else if (value instanceof Date)
193            {
194                XMLUtils.createElement(contentHandler, fieldName, ParameterHelper.valueToString(value));
195            }
196            else if (value instanceof Map)
197            {
198                AttributesImpl valueAttrs = new AttributesImpl();
199                
200                Map<?, ?> valueMap = (Map<?, ?>) value;
201                for (Map.Entry<?, ?> entry : valueMap.entrySet())
202                {
203                    valueAttrs.addCDATAAttribute(entry.getKey().toString(), entry.getValue().toString());
204                }
205                
206                XMLUtils.createElement(contentHandler, fieldName, valueAttrs);
207            }
208            else if (value instanceof Collection)
209            {
210                XMLUtils.startElement(contentHandler, fieldName);
211                Collection<?> values = (Collection<?>) value;
212                for (Object valueObj : values)
213                {
214                    XMLUtils.createElement(contentHandler, "value", valueObj.toString());
215                }
216                XMLUtils.endElement(contentHandler, fieldName);
217            }
218            else
219            {
220                XMLUtils.createElement(contentHandler, fieldName, value.toString());
221            }
222        }
223    }
224    
225    /**
226     * Generate a human-readable representation of a result field for a content.
227     * @param resultField The result field.
228     * @param value The content value.
229     * @param language The language.
230     * @throws SAXException If an error occurs generating the XML.
231     */
232    protected void saxLabel(ResultField resultField, Object value, String language) throws SAXException
233    {
234        String fieldName = resultField.getId();
235        MetadataType type = resultField.getType();
236        Enumerator enumerator = resultField.getEnumerator();
237        
238        if (value != null)
239        {
240            if (type == MetadataType.USER)
241            {
242                generateUserLabel(resultField, value);
243            }
244            else if (type == MetadataType.CONTENT || type == MetadataType.SUB_CONTENT)
245            {
246                generateContentLabel(resultField, value);
247            }
248            else if (enumerator != null)
249            {
250                generateEnumeratedLabel(resultField, value);
251            }
252            else if (value instanceof I18nizableText)
253            {
254                ((I18nizableText) value).toSAX(contentHandler, fieldName);
255            }
256            else if (value instanceof Date)
257            {
258                XMLUtils.createElement(contentHandler, fieldName, ParameterHelper.valueToString(value));
259            }
260            else if (value instanceof Map)
261            {
262                Map<?, ?> valueMap = (Map<?, ?>) value;
263                XMLUtils.createElement(contentHandler, fieldName, StringUtils.join(valueMap.values(), ", "));
264            }
265            else if (value instanceof Collection)
266            {
267                Collection<?> values = (Collection<?>) value;
268                XMLUtils.createElement(contentHandler, fieldName, StringUtils.join(values, ", "));
269            }
270            else
271            {
272                XMLUtils.createElement(contentHandler, fieldName, value.toString());
273            }
274        }
275    }
276    
277    /**
278     * Generate the label of an user value.
279     * @param resultField The result field.
280     * @param value The content value.
281     * @throws SAXException If an error occurs generating the XML.
282     */
283    @SuppressWarnings("unchecked")
284    protected void generateUserLabel(ResultField resultField, Object value) throws SAXException
285    {
286        String fieldName = resultField.getId();
287        
288        String login;
289        String populationId;
290        if (value instanceof String)
291        {
292            String[] parts = ((String) value).split("#");
293            login = parts[0];
294            populationId = parts[1];
295        }
296        else
297        {
298            login = ((Map<String, String>) value).get("login");
299            populationId = ((Map<String, String>) value).get("populationId");
300        }
301        
302        User user = _userManager.getUser(populationId, login);
303        if (user != null)
304        {
305            XMLUtils.createElement(contentHandler, fieldName, user.getFullName());
306        }
307    }
308    
309    /**
310     * Generate the label of an content value.
311     * @param resultField The result field.
312     * @param value The content value.
313     * @throws SAXException If an error occurs generating the XML.
314     */
315    protected void generateContentLabel(ResultField resultField, Object value) throws SAXException
316    {
317        try
318        {
319            String fieldName = resultField.getId();
320            
321            Content content = _resolver.resolveById(value.toString());
322            XMLUtils.createElement(contentHandler, fieldName, content.getTitle());
323        }
324        catch (AmetysRepositoryException e)
325        {
326            // Ignore
327        }
328    }
329    
330    /**
331     * Generate the label of an enumerated value.
332     * @param resultField The result field.
333     * @param value The content value.
334     * @throws SAXException If an error occurs generating the XML.
335     */
336    protected void generateEnumeratedLabel(ResultField resultField, Object value) throws SAXException
337    {
338        String fieldName = resultField.getId();
339        Enumerator enumerator = resultField.getEnumerator();
340        
341        if (value instanceof Collection)
342        {
343            Collection<?> values = (Collection<?>) value;
344            XMLUtils.startElement(contentHandler, fieldName);
345            int i = 0;
346            for (Object valueObj : values)
347            {
348                try
349                {
350                    I18nizableText entry = enumerator.getEntry(valueObj.toString());
351                    if (entry != null)
352                    {
353                        if (i > 0)
354                        {
355                            XMLUtils.data(contentHandler, ", ");
356                        }
357                        entry.toSAX(contentHandler);
358                        i++;
359                    }
360                }
361                catch (Exception e)
362                {
363                    // Ignore.
364                }
365            }
366            XMLUtils.endElement(contentHandler, fieldName);
367        }
368        else
369        {
370            try
371            {
372                I18nizableText entry = enumerator.getEntry(value.toString());
373                if (entry != null)
374                {
375                    entry.toSAX(contentHandler, fieldName);
376                }
377            }
378            catch (Exception e)
379            {
380                // Ignore.
381            }
382        }
383    }
384    
385}