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