001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.jsp.search.result;
029
030import org.opencms.i18n.CmsEncoder;
031import org.opencms.jsp.search.controller.I_CmsSearchControllerFacetField;
032import org.opencms.jsp.search.controller.I_CmsSearchControllerFacetQuery;
033import org.opencms.jsp.search.controller.I_CmsSearchControllerFacetRange;
034import org.opencms.main.CmsLog;
035import org.opencms.util.CmsCollectionsGenericWrapper;
036
037import java.util.Arrays;
038import java.util.Collection;
039import java.util.HashMap;
040import java.util.List;
041import java.util.Map;
042
043import org.apache.commons.collections.Transformer;
044import org.apache.commons.logging.Log;
045
046/**
047 * State parameter wrapper that allows to manipulate the request parameters representing the state
048 * of the current search. It can be used to generate links for an adjusted search.
049 */
050public class CmsSearchStateParameters implements I_CmsSearchStateParameters {
051
052    /** Logger for the class. */
053    protected static final Log LOG = CmsLog.getLog(CmsSearchStateParameters.class);
054
055    /** Map of request parameters, representing the search's state. */
056    Map<String, String[]> m_params;
057    /** The result of the search. */
058    I_CmsSearchResultWrapper m_result;
059
060    /** Map with page numbers as keys and the according state parameters as values. */
061    Map<String, I_CmsSearchStateParameters> m_paginationMap;
062    /** Map from sort options to state parameters. */
063    Map<String, I_CmsSearchStateParameters> m_sortingMap;
064    /** Map from facet names to state parameters without the filter queries for the facet. */
065    Map<String, I_CmsSearchStateParameters> m_resetFacetMap;
066    /** Map from facet names to state parameters with parameters for ignoring the facet's limit added. */
067    Map<String, I_CmsSearchStateParameters> m_ignoreLimitFacetMap;
068    /** Map new queries to state parameters with the query replaced by the new query. */
069    Map<String, I_CmsSearchStateParameters> m_newQueryMap;
070    /** Map from facet names to state parameters with parameters for ignoring the facet's limit removed. */
071    Map<String, I_CmsSearchStateParameters> m_respectLimitFacetMap;
072    /** Map from facet names to a map from facet items to state parameters with the item unchecked. */
073    Map<String, Map<String, I_CmsSearchStateParameters>> m_uncheckFacetMap;
074    /** Map from facet names to a map from facet items to state parameters with the item checked. */
075    Map<String, Map<String, I_CmsSearchStateParameters>> m_checkFacetMap;
076
077    /** Constructor for a state parameters object.
078     * @param result The search result, according to which the parameters are manipulated.
079     * @param params The original parameter set.
080     */
081    public CmsSearchStateParameters(final I_CmsSearchResultWrapper result, final Map<String, String[]> params) {
082
083        m_params = params;
084        m_result = result;
085    }
086
087    /** Converts a parameter map to the parameter string.
088     * @param parameters the parameter map.
089     * @return the parameter string.
090     */
091    public static String paramMapToString(final Map<String, String[]> parameters) {
092
093        final StringBuffer result = new StringBuffer();
094        for (final String key : parameters.keySet()) {
095            String[] values = parameters.get(key);
096            if (null == values) {
097                result.append(key).append('&');
098            } else {
099                for (final String value : parameters.get(key)) {
100                    result.append(key).append('=').append(CmsEncoder.encode(value)).append('&');
101                }
102            }
103        }
104        // remove last '&'
105        if (result.length() > 0) {
106            result.setLength(result.length() - 1);
107        }
108        return result.toString();
109    }
110
111    /**
112     * @see org.opencms.jsp.search.result.I_CmsSearchStateParameters#getAddIgnoreFacetLimit()
113     */
114    public Map<String, I_CmsSearchStateParameters> getAddIgnoreFacetLimit() {
115
116        if (m_ignoreLimitFacetMap == null) {
117            m_ignoreLimitFacetMap = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() {
118
119                @Override
120                public Object transform(final Object facet) {
121
122                    final Map<String, String[]> parameters = new HashMap<String, String[]>(m_params);
123                    String facetParamKey = null;
124                    try {
125                        facetParamKey = m_result.getController().getFieldFacets().getFieldFacetController().get(
126                            facet).getConfig().getIgnoreMaxParamKey();
127                    } catch (Exception e) {
128                        // Facet did not exist
129                        LOG.warn(Messages.get().getBundle().key(Messages.LOG_FACET_NOT_CONFIGURED_1, facet), e);
130                    }
131                    if ((facetParamKey != null) && !parameters.containsKey(facetParamKey)) {
132                        parameters.put(facetParamKey, null);
133                    }
134                    return new CmsSearchStateParameters(m_result, parameters);
135                }
136            });
137        }
138        return m_ignoreLimitFacetMap;
139    }
140
141    /**
142     * @see org.opencms.jsp.search.result.I_CmsSearchStateParameters#getCheckFacetItem()
143     */
144    @Override
145    public Map<String, Map<String, I_CmsSearchStateParameters>> getCheckFacetItem() {
146
147        if (m_checkFacetMap == null) {
148            m_checkFacetMap = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() {
149
150                @Override
151                public Object transform(final Object facet) {
152
153                    Map<String, I_CmsSearchStateParameters> m_checkEntries = CmsCollectionsGenericWrapper.createLazyMap(
154                        new Transformer() {
155
156                            @Override
157                            public Object transform(final Object facetItem) {
158
159                                final Map<String, String[]> parameters = new HashMap<String, String[]>(m_params);
160                                String facetParamKey = getFacetParamKey((String)facet);
161                                if (facetParamKey != null) { // otherwise the facet was not configured, thus no item can be added
162                                    if (parameters.containsKey(facetParamKey)) {
163                                        String[] values = parameters.get(facetParamKey);
164                                        if (!Arrays.asList(values).contains(facetItem)) {
165                                            String[] newValues = new String[Arrays.asList(values).size() + 1];
166                                            for (int i = 0; i < (values.length); i++) {
167                                                newValues[i] = values[i];
168                                            }
169                                            newValues[values.length] = (String)facetItem;
170                                            parameters.put(facetParamKey, newValues);
171                                        }
172                                    } else {
173                                        parameters.put(facetParamKey, new String[] {(String)facetItem});
174                                    }
175
176                                }
177                                return new CmsSearchStateParameters(m_result, parameters);
178                            }
179                        });
180                    return m_checkEntries;
181                }
182            });
183        }
184        return m_checkFacetMap;
185
186    }
187
188    /**
189     * @see org.opencms.jsp.search.result.I_CmsSearchStateParameters#getNewQuery()
190     */
191    @Override
192    public Map<String, I_CmsSearchStateParameters> getNewQuery() {
193
194        if (m_newQueryMap == null) {
195            m_newQueryMap = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() {
196
197                @Override
198                public Object transform(final Object queryString) {
199
200                    final Map<String, String[]> parameters = new HashMap<String, String[]>(m_params);
201                    String queryKey = m_result.getController().getCommon().getConfig().getQueryParam();
202                    if (parameters.containsKey(queryKey)) {
203                        parameters.remove(queryKey);
204                    }
205                    parameters.put(queryKey, new String[] {(String)queryString});
206                    return new CmsSearchStateParameters(m_result, parameters);
207                }
208            });
209        }
210        return m_newQueryMap;
211    }
212
213    /**
214     * @see org.opencms.jsp.search.result.I_CmsSearchStateParameters#getRemoveIgnoreFacetLimit()
215     */
216    public Map<String, I_CmsSearchStateParameters> getRemoveIgnoreFacetLimit() {
217
218        if (m_ignoreLimitFacetMap == null) {
219            m_ignoreLimitFacetMap = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() {
220
221                @Override
222                public Object transform(final Object facet) {
223
224                    final Map<String, String[]> parameters = new HashMap<String, String[]>(m_params);
225                    String facetParamKey = null;
226                    try {
227                        facetParamKey = m_result.getController().getFieldFacets().getFieldFacetController().get(
228                            facet).getConfig().getIgnoreMaxParamKey();
229                    } catch (Exception e) {
230                        // Facet did not exist
231                        LOG.warn(Messages.get().getBundle().key(Messages.LOG_FACET_NOT_CONFIGURED_1, facet), e);
232                    }
233                    if ((facetParamKey != null) && parameters.containsKey(facetParamKey)) {
234                        parameters.remove(facetParamKey);
235                    }
236                    return new CmsSearchStateParameters(m_result, parameters);
237                }
238            });
239        }
240        return m_ignoreLimitFacetMap;
241    }
242
243    /**
244     * @see org.opencms.jsp.search.result.I_CmsSearchStateParameters#getResetAllFacetStates()
245     */
246    @Override
247    public I_CmsSearchStateParameters getResetAllFacetStates() {
248
249        final Map<String, String[]> parameters = new HashMap<String, String[]>(m_params);
250        // Remove selected entries from field facets
251        Collection<I_CmsSearchControllerFacetField> fieldFacets = m_result.getController().getFieldFacets().getFieldFacetControllers();
252        for (I_CmsSearchControllerFacetField facet : fieldFacets) {
253            String facetParamKey = facet.getConfig().getParamKey();
254            if (parameters.containsKey(facetParamKey)) {
255                parameters.remove(facetParamKey);
256            }
257        }
258        // Remove selected entries from range facets
259        Collection<I_CmsSearchControllerFacetRange> rangeFacets = m_result.getController().getRangeFacets().getRangeFacetControllers();
260        for (I_CmsSearchControllerFacetRange facet : rangeFacets) {
261            String facetParamKey = facet.getConfig().getParamKey();
262            if (parameters.containsKey(facetParamKey)) {
263                parameters.remove(facetParamKey);
264            }
265        }
266        // Remove selected entries from the query facet
267        I_CmsSearchControllerFacetQuery facet = m_result.getController().getQueryFacet();
268        if (null != facet) {
269            String facetParamKey = facet.getConfig().getParamKey();
270            if (parameters.containsKey(facetParamKey)) {
271                parameters.remove(facetParamKey);
272            }
273        }
274        return new CmsSearchStateParameters(m_result, parameters);
275    }
276
277    /**
278     * @see org.opencms.jsp.search.result.I_CmsSearchStateParameters#getResetFacetState()
279     */
280    @Override
281    public Map<String, I_CmsSearchStateParameters> getResetFacetState() {
282
283        if (m_resetFacetMap == null) {
284            m_resetFacetMap = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() {
285
286                @Override
287                public Object transform(final Object facet) {
288
289                    final Map<String, String[]> parameters = new HashMap<String, String[]>(m_params);
290                    String facetParamKey = getFacetParamKey((String)facet);
291                    if ((facetParamKey != null) && parameters.containsKey(facetParamKey)) {
292                        parameters.remove(facetParamKey);
293                    }
294                    return new CmsSearchStateParameters(m_result, parameters);
295                }
296            });
297        }
298        return m_resetFacetMap;
299    }
300
301    /**
302     * @see org.opencms.jsp.search.result.I_CmsSearchStateParameters#getSetPage()
303     */
304    @Override
305    public Map<String, I_CmsSearchStateParameters> getSetPage() {
306
307        if (m_paginationMap == null) {
308            m_paginationMap = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() {
309
310                @Override
311                public Object transform(final Object page) {
312
313                    final Map<String, String[]> parameters = new HashMap<String, String[]>(m_params);
314                    parameters.put(
315                        m_result.getController().getPagination().getConfig().getPageParam(),
316                        new String[] {(String)page});
317                    return new CmsSearchStateParameters(m_result, parameters);
318                }
319            });
320        }
321        return m_paginationMap;
322    }
323
324    /**
325     * @see org.opencms.jsp.search.result.I_CmsSearchStateParameters#getSetSortOption()
326     */
327    @Override
328    public Map<String, I_CmsSearchStateParameters> getSetSortOption() {
329
330        if (m_sortingMap == null) {
331            m_sortingMap = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() {
332
333                @Override
334                public Object transform(final Object sortOption) {
335
336                    final Map<String, String[]> parameters = new HashMap<String, String[]>(m_params);
337                    m_result.getController().addParametersForCurrentState(parameters);
338                    parameters.put(
339                        m_result.getController().getSorting().getConfig().getSortParam(),
340                        new String[] {(String)sortOption});
341                    return new CmsSearchStateParameters(m_result, parameters);
342                }
343            });
344        }
345        return m_sortingMap;
346    }
347
348    /**
349     * @see org.opencms.jsp.search.result.I_CmsSearchStateParameters#getUncheckFacetItem()
350     */
351    @Override
352    public Map<String, Map<String, I_CmsSearchStateParameters>> getUncheckFacetItem() {
353
354        if (m_uncheckFacetMap == null) {
355            m_uncheckFacetMap = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() {
356
357                @Override
358                public Object transform(final Object facet) {
359
360                    Map<String, I_CmsSearchStateParameters> m_uncheckEntries = CmsCollectionsGenericWrapper.createLazyMap(
361                        new Transformer() {
362
363                            @Override
364                            public Object transform(final Object facetItem) {
365
366                                final Map<String, String[]> parameters = new HashMap<String, String[]>(m_params);
367                                String facetParamKey = getFacetParamKey((String)facet);
368                                if ((facetParamKey != null) && parameters.containsKey(facetParamKey)) {
369                                    String[] values = parameters.get(facetParamKey);
370                                    List<String> valueList = Arrays.asList(values);
371                                    String item = (String)facetItem;
372                                    if (valueList.contains(facetItem)) {
373                                        String[] newValues = new String[valueList.size() - 1];
374                                        int i = 0;
375                                        for (String value : valueList) {
376                                            if (!value.equals(item)) {
377                                                newValues[i++] = value;
378                                            }
379                                        }
380                                        parameters.put(facetParamKey, newValues);
381                                    }
382                                }
383                                return new CmsSearchStateParameters(m_result, parameters);
384                            }
385                        });
386                    return m_uncheckEntries;
387                }
388            });
389        }
390        return m_uncheckFacetMap;
391    }
392
393    /**
394     * @see java.lang.Object#toString()
395     */
396    @Override
397    public String toString() {
398
399        return paramMapToString(m_params);
400    }
401
402    /**
403     * Returns the parameter key of the facet with the given name.
404     * @param facet the facet's name.
405     * @return the parameter key for the facet.
406     */
407    String getFacetParamKey(String facet) {
408
409        I_CmsSearchControllerFacetField fieldFacet = m_result.getController().getFieldFacets().getFieldFacetController().get(
410            facet);
411        if (fieldFacet != null) {
412            return fieldFacet.getConfig().getParamKey();
413        }
414        I_CmsSearchControllerFacetRange rangeFacet = m_result.getController().getRangeFacets().getRangeFacetController().get(
415            facet);
416        if (rangeFacet != null) {
417            return rangeFacet.getConfig().getParamKey();
418        }
419        I_CmsSearchControllerFacetQuery queryFacet = m_result.getController().getQueryFacet();
420        if ((queryFacet != null) && queryFacet.getConfig().getName().equals(facet)) {
421            return queryFacet.getConfig().getParamKey();
422        }
423
424        // Facet did not exist
425        LOG.warn(Messages.get().getBundle().key(Messages.LOG_FACET_NOT_CONFIGURED_1, facet), new Throwable());
426
427        return null;
428    }
429
430}