001/*
002 *  Copyright 2019 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.web.frontoffice.search.requesttime.input.impl;
017
018import java.util.Arrays;
019import java.util.Collections;
020import java.util.Comparator;
021import java.util.Enumeration;
022import java.util.List;
023import java.util.Map;
024import java.util.stream.Collectors;
025import java.util.stream.Stream;
026
027import org.apache.cocoon.environment.Request;
028import org.apache.commons.lang3.StringUtils;
029import org.apache.commons.lang3.tuple.Pair;
030import org.apache.commons.lang3.tuple.Triple;
031import org.slf4j.Logger;
032import org.slf4j.LoggerFactory;
033
034import org.ametys.cms.search.Sort;
035
036/**
037 * The search user inputs from a submitted form.
038 */
039public class FormSearchUserInputs extends AbstractSearchUserInputs
040{
041    /** Logger */
042    protected static final Logger __LOGGER = LoggerFactory.getLogger(FormSearchUserInputs.class);
043    
044    private static final String USER_INPUT_PREFIX = "user.input.";
045    
046    /** The prefix for the criteria */
047    public static final String CRITERION_PREFIX = USER_INPUT_PREFIX + "criterion.";
048    /** The prefix for the facets */
049    public static final String FACET_PREFIX = USER_INPUT_PREFIX + "facet.";
050    /** The prefix for the sorts */
051    public static final String SORT_PREFIX = USER_INPUT_PREFIX + "sort.";
052    
053    /**
054     * The constructor for search user inputs from a form.
055     * @param request The request
056     */
057    public FormSearchUserInputs(Request request)
058    {
059        _criteria = _retrieveUserCriteria(request);
060        _facets = _retrieveUserFacets(request);
061        _sorts = _retrieveUserSorts(request);
062    }
063    
064    /**
065     * Retrieves the user criteria
066     * @param request the request
067     * @return the user criteria
068     */
069    protected Map<String, Object> _retrieveUserCriteria(Request request)
070    {
071        return Collections.list((Enumeration<String>) request.getParameterNames())
072                .stream()
073                .filter(param -> param.startsWith(CRITERION_PREFIX))
074                .filter(param -> _isNotBlank(request, param))
075                .collect(Collectors.toMap(
076                    param -> StringUtils.removeStart(param, CRITERION_PREFIX), 
077                    param -> _requestParameterToTypedObject(request, param)
078                ));
079    }
080    
081    /**
082     * Returns <code>true</code> if at least one of the parameter values is not blank
083     * @param request the request
084     * @param param the parameter name
085     * @return <code>true</code> if at least one of the parameter values is not blank
086     */
087    protected boolean _isNotBlank(Request request, String param)
088    {
089        String[] parameterValues = request.getParameterValues(param);
090        if (parameterValues == null)
091        {
092            return false;
093        }
094        
095        for (String parameterValue : parameterValues)
096        {
097            if (StringUtils.isNotBlank(parameterValue))
098            {
099                // at least one is not blank
100                return true;
101            }
102        }
103        
104        // all values are blank
105        return false;
106    }
107    
108    /**
109     * Gets a typed object from request parameters
110     * @param request the request
111     * @param param the parameter name
112     * @return a typed object
113     */
114    protected Object _requestParameterToTypedObject(Request request, String param)
115    {
116        String[] parameterValues = request.getParameterValues(param); // cannot be null due to #_isNotBlank
117        List<String> notBlankValues = Stream.of(parameterValues)
118                .filter(StringUtils::isNotBlank)
119                .collect(Collectors.toList());
120        
121        if (notBlankValues.size() == 1)
122        {
123            return notBlankValues.get(0);
124        }
125        else
126        {
127            return notBlankValues;
128        }
129    }
130    
131    /**
132     * Retrieves the user facets
133     * @param request the request
134     * @return the user facets
135     */
136    protected Map<String, List<String>> _retrieveUserFacets(Request request)
137    {
138        return Collections.list((Enumeration<String>) request.getParameterNames())
139                .stream()
140                .filter(param -> param.startsWith(FACET_PREFIX))
141                .collect(Collectors.toMap(
142                    param -> StringUtils.removeStart(param, FACET_PREFIX), 
143                    param -> Arrays.asList(StringUtils.split(request.getParameter(param), ','))
144                ));
145    }
146    
147    /**
148     * Retrieves the user sorts
149     * @param request the request
150     * @return the user sorts
151     */
152    protected List<Pair<String, Sort.Order>> _retrieveUserSorts(Request request)
153    {
154        return Collections.list((Enumeration<String>) request.getParameterNames()) // parameters have the following format: "user.input.sort.foo=ASC,1"
155                .stream()
156                .filter(param -> param.startsWith(SORT_PREFIX))
157                .map(param -> Pair.of(StringUtils.removeStart(param, SORT_PREFIX), request.getParameter(param)))
158                .peek(pair ->
159                {
160                    String sortValue = pair.getRight();
161                    if (!sortValue.contains(","))
162                    {
163                        __LOGGER.warn("'{}{}' parameter does not have a valid value: '{}'. Missing a \",\" character to distinguish the sort (ASC or DESC) from the sort order (integer)", SORT_PREFIX, pair.getLeft(), sortValue);
164                    }
165                })
166                .filter(pair -> pair.getRight().contains(","))
167                .map(pair ->
168                {
169                    String sortValue = pair.getRight();
170                    String[] strings = sortValue.split(",");
171                    return Triple.of(pair.getLeft(), strings[0]/*ASC or DESC*/, strings[1]/*order in the list of all sorts*/);
172                })
173                .sorted(Comparator.comparing(triple -> Integer.parseInt(triple.getRight())))
174                .map(triple -> Pair.of(triple.getLeft(), Sort.Order.valueOf(triple.getMiddle())))
175                .collect(Collectors.toList());
176    }
177}