001/*
002 *  Copyright 2015 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.Collections;
020import java.util.List;
021import java.util.Map;
022import java.util.Optional;
023import java.util.Set;
024
025import org.apache.avalon.framework.service.ServiceException;
026import org.apache.avalon.framework.service.ServiceManager;
027import org.apache.cocoon.ProcessingException;
028import org.apache.cocoon.generation.ServiceableGenerator;
029import org.apache.cocoon.xml.AttributesImpl;
030import org.apache.cocoon.xml.XMLUtils;
031import org.xml.sax.SAXException;
032
033import org.ametys.cms.contenttype.ContentTypesHelper;
034import org.ametys.cms.search.model.SearchModel;
035import org.ametys.cms.search.solr.SolrContentQueryHelper;
036import org.ametys.cms.search.ui.model.ColumnHelper;
037import org.ametys.cms.search.ui.model.ColumnHelper.Column;
038import org.ametys.cms.search.ui.model.SearchUIModelExtensionPoint;
039import org.ametys.core.util.ServerCommHelper;
040import org.ametys.runtime.i18n.I18nizableText;
041import org.ametys.runtime.model.ModelViewItem;
042import org.ametys.runtime.model.ViewHelper;
043import org.ametys.runtime.model.ViewItem;
044import org.ametys.runtime.model.ViewItemAccessor;
045import org.ametys.runtime.model.ViewItemContainer;
046
047/**
048 * SAX the columns of a search tool model.
049 */
050public class ModelColumnsGenerator extends ServiceableGenerator
051{
052    /** The extenstion point for search UI */
053    protected SearchUIModelExtensionPoint _searchModelManager;
054    /** The servercomm helper */
055    protected ServerCommHelper _serverCommHelper;
056    /** The content type helper. */
057    protected ContentTypesHelper _cTypeHelper;
058    /** The helper for columns */
059    protected ColumnHelper _columnHelper;
060    
061    @Override
062    public void service(ServiceManager smanager) throws ServiceException
063    {
064        super.service(smanager);
065        _searchModelManager = (SearchUIModelExtensionPoint) smanager.lookup(SearchUIModelExtensionPoint.ROLE);
066        _serverCommHelper = (ServerCommHelper) manager.lookup(ServerCommHelper.ROLE);
067        _cTypeHelper = (ContentTypesHelper) smanager.lookup(ContentTypesHelper.ROLE);
068        _columnHelper = (ColumnHelper) smanager.lookup(ColumnHelper.ROLE);
069    }
070    
071    @Override
072    public void generate() throws IOException, SAXException, ProcessingException
073    {
074        Map<String, Object> jsParameters = _serverCommHelper.getJsParameters();
075        
076        contentHandler.startDocument();
077        saxColumns(jsParameters);
078        contentHandler.endDocument();
079    }
080    
081    /**
082     * Get search model.
083     * @param jsParameters JS parameters
084     * @return the search model
085     */
086    protected SearchModel getSearchModel(Map<String, Object> jsParameters)
087    {
088        String modelId = (String) jsParameters.get("model");
089        return _searchModelManager.getExtension(modelId);
090    }
091    
092    /**
093     * Sax search columns.
094     * @param jsParameters JS parameters
095     * @throws SAXException if an error occurs
096     */
097    protected void saxColumns(Map<String, Object> jsParameters) throws SAXException
098    {
099        @SuppressWarnings("unchecked")
100        Map<String, Object> contextualParameters = (Map<String, Object>) jsParameters.get("contextualParameters");
101        
102        SearchModel model = getSearchModel(jsParameters);
103        List<Column> columns = _getColumns(model, jsParameters, contextualParameters);
104        ViewItemContainer resultItems = model.getResultItems(contextualParameters);
105
106        XMLUtils.startElement(contentHandler, "columns");
107        _saxColumns(resultItems, columns);
108        XMLUtils.endElement(contentHandler, "columns");
109    }
110    
111    private List<Column> _getColumns(SearchModel model, Map<String, Object> jsParameters, Map<String, Object> contextualParameters)
112    {
113        Set<String> cTypes;
114        if ("search-ui.solr".equals(jsParameters.get("model")))
115        {
116            cTypes = SolrContentQueryHelper.getContentTypes(jsParameters);
117        }
118        else
119        {
120            cTypes = model.getContentTypes(contextualParameters);
121        }
122        Set<String> cTypeIds = _cTypeHelper.getCommonAncestors(cTypes);
123        List<Column> columns = getColumnsFromParameters(jsParameters, cTypeIds);
124        return columns;
125    }
126    
127    private void _saxColumns(ViewItemAccessor viewItemAccessor, List<Column> columns) throws SAXException
128    {
129        for (ViewItem viewItem : viewItemAccessor.getViewItems())
130        {
131            if (viewItem instanceof ViewItemAccessor itemAccessor && !itemAccessor.getViewItems().isEmpty())
132            {
133                _saxColumns(itemAccessor, columns);
134            }
135            else if (viewItem instanceof ModelViewItem modelViewItem)
136            {
137                String modelViewItemPath = ViewHelper.getModelViewItemPath(modelViewItem);
138                Optional<Column> column = columns.stream()
139                                                 .filter(c -> c.getId().equals(modelViewItemPath))
140                                                 .findFirst();
141                
142                if (columns.isEmpty() || column.isPresent())
143                {
144                    AttributesImpl attrs = new AttributesImpl();
145                    attrs.addCDATAAttribute("id", ViewHelper.getModelViewItemPath(modelViewItem));
146                    attrs.addCDATAAttribute("type", modelViewItem.getDefinition().getType().getId());
147                    
148                    XMLUtils.startElement(contentHandler, "column", attrs);
149                    
150                    column.flatMap(Column::getLabel)
151                          .map(I18nizableText::new)
152                          .orElseGet(modelViewItem::getLabel)
153                          .toSAX(contentHandler);
154                    
155                    XMLUtils.endElement(contentHandler, "column");
156                }
157            }
158        }
159    }
160    
161    /**
162     * Get the columns from JS parameters
163     * @param jsParameters The JS parameters
164     * @param contentTypes The content type
165     * @return the requested columns
166     */
167    @SuppressWarnings("unchecked")
168    protected List<Column> getColumnsFromParameters(Map<String, Object> jsParameters, Set<String> contentTypes)
169    {
170        Map<String, Object> values = (Map<String, Object>) jsParameters.get("values");
171
172        if (values != null && values.containsKey("columns"))
173        {
174            Object columnsAsObj = values.get("columns");
175            if (columnsAsObj instanceof String)
176            {
177                return _columnHelper.getColumns((String) columnsAsObj, contentTypes);
178            }
179            else
180            {
181                return _columnHelper.getColumns((List<String>) columnsAsObj, contentTypes);
182            }
183        }
184        return Collections.EMPTY_LIST;
185    }
186}