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    /** Map from additional parameter names to the values. */
077    Map<String, Map<String, I_CmsSearchStateParameters>> m_setAdditionalParamsMap;
078    /** Map from additional parameter names to the values. */
079    Map<String, I_CmsSearchStateParameters> m_unsetAdditionalParamsMap;
080
081    /** Constructor for a state parameters object.
082     * @param result The search result, according to which the parameters are manipulated.
083     * @param params The original parameter set.
084     */
085    public CmsSearchStateParameters(final I_CmsSearchResultWrapper result, final Map<String, String[]> params) {
086
087        m_params = params;
088        m_result = result;
089    }
090
091    /** Converts a parameter map to the parameter string.
092     * @param parameters the parameter map.
093     * @return the parameter string.
094     */
095    public static String paramMapToString(final Map<String, String[]> parameters) {
096
097        final StringBuffer result = new StringBuffer();
098        for (final String key : parameters.keySet()) {
099            String[] values = parameters.get(key);
100            if (null == values) {
101                result.append(key).append('&');
102            } else {
103                for (final String value : parameters.get(key)) {
104                    result.append(key).append('=').append(CmsEncoder.encode(value)).append('&');
105                }
106            }
107        }
108        // remove last '&'
109        if (result.length() > 0) {
110            result.setLength(result.length() - 1);
111        }
112        return result.toString();
113    }
114
115    /**
116     * @see org.opencms.jsp.search.result.I_CmsSearchStateParameters#getAddIgnoreFacetLimit()
117     */
118    public Map<String, I_CmsSearchStateParameters> getAddIgnoreFacetLimit() {
119
120        if (m_ignoreLimitFacetMap == null) {
121            m_ignoreLimitFacetMap = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() {
122
123                @Override
124                public Object transform(final Object facet) {
125
126                    final Map<String, String[]> parameters = new HashMap<String, String[]>(m_params);
127                    String facetParamKey = null;
128                    try {
129                        facetParamKey = m_result.getController().getFieldFacets().getFieldFacetController().get(
130                            facet).getConfig().getIgnoreMaxParamKey();
131                    } catch (Exception e) {
132                        // Facet did not exist
133                        LOG.warn(Messages.get().getBundle().key(Messages.LOG_FACET_NOT_CONFIGURED_1, facet), e);
134                    }
135                    if ((facetParamKey != null) && !parameters.containsKey(facetParamKey)) {
136                        parameters.put(facetParamKey, null);
137                    }
138                    return new CmsSearchStateParameters(m_result, parameters);
139                }
140            });
141        }
142        return m_ignoreLimitFacetMap;
143    }
144
145    /**
146     * @see org.opencms.jsp.search.result.I_CmsSearchStateParameters#getCheckFacetItem()
147     */
148    @Override
149    public Map<String, Map<String, I_CmsSearchStateParameters>> getCheckFacetItem() {
150
151        if (m_checkFacetMap == null) {
152            m_checkFacetMap = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() {
153
154                @Override
155                public Object transform(final Object facet) {
156
157                    Map<String, I_CmsSearchStateParameters> m_checkEntries = CmsCollectionsGenericWrapper.createLazyMap(
158                        new Transformer() {
159
160                            @Override
161                            public Object transform(final Object facetItem) {
162
163                                final Map<String, String[]> parameters = new HashMap<String, String[]>(m_params);
164                                String facetParamKey = getFacetParamKey((String)facet);
165                                if (facetParamKey != null) { // otherwise the facet was not configured, thus no item can be added
166                                    if (parameters.containsKey(facetParamKey)) {
167                                        String[] values = parameters.get(facetParamKey);
168                                        if (!Arrays.asList(values).contains(facetItem)) {
169                                            String[] newValues = new String[Arrays.asList(values).size() + 1];
170                                            for (int i = 0; i < (values.length); i++) {
171                                                newValues[i] = values[i];
172                                            }
173                                            newValues[values.length] = (String)facetItem;
174                                            parameters.put(facetParamKey, newValues);
175                                        }
176                                    } else {
177                                        parameters.put(facetParamKey, new String[] {(String)facetItem});
178                                    }
179
180                                }
181                                return new CmsSearchStateParameters(m_result, parameters);
182                            }
183                        });
184                    return m_checkEntries;
185                }
186            });
187        }
188        return m_checkFacetMap;
189
190    }
191
192    /**
193     * @see org.opencms.jsp.search.result.I_CmsSearchStateParameters#getNewQuery()
194     */
195    @Override
196    public Map<String, I_CmsSearchStateParameters> getNewQuery() {
197
198        if (m_newQueryMap == null) {
199            m_newQueryMap = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() {
200
201                @Override
202                public Object transform(final Object queryString) {
203
204                    final Map<String, String[]> parameters = new HashMap<String, String[]>(m_params);
205                    String queryKey = m_result.getController().getCommon().getConfig().getQueryParam();
206                    if (parameters.containsKey(queryKey)) {
207                        parameters.remove(queryKey);
208                    }
209                    parameters.put(queryKey, new String[] {(String)queryString});
210                    return new CmsSearchStateParameters(m_result, parameters);
211                }
212            });
213        }
214        return m_newQueryMap;
215    }
216
217    /**
218     * @see org.opencms.jsp.search.result.I_CmsSearchStateParameters#getRemoveIgnoreFacetLimit()
219     */
220    public Map<String, I_CmsSearchStateParameters> getRemoveIgnoreFacetLimit() {
221
222        if (m_ignoreLimitFacetMap == null) {
223            m_ignoreLimitFacetMap = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() {
224
225                @Override
226                public Object transform(final Object facet) {
227
228                    final Map<String, String[]> parameters = new HashMap<String, String[]>(m_params);
229                    String facetParamKey = null;
230                    try {
231                        facetParamKey = m_result.getController().getFieldFacets().getFieldFacetController().get(
232                            facet).getConfig().getIgnoreMaxParamKey();
233                    } catch (Exception e) {
234                        // Facet did not exist
235                        LOG.warn(Messages.get().getBundle().key(Messages.LOG_FACET_NOT_CONFIGURED_1, facet), e);
236                    }
237                    if ((facetParamKey != null) && parameters.containsKey(facetParamKey)) {
238                        parameters.remove(facetParamKey);
239                    }
240                    return new CmsSearchStateParameters(m_result, parameters);
241                }
242            });
243        }
244        return m_ignoreLimitFacetMap;
245    }
246
247    /**
248     * @see org.opencms.jsp.search.result.I_CmsSearchStateParameters#getResetAllFacetStates()
249     */
250    @Override
251    public I_CmsSearchStateParameters getResetAllFacetStates() {
252
253        final Map<String, String[]> parameters = new HashMap<String, String[]>(m_params);
254        // Remove selected entries from field facets
255        Collection<I_CmsSearchControllerFacetField> fieldFacets = m_result.getController().getFieldFacets().getFieldFacetControllers();
256        for (I_CmsSearchControllerFacetField facet : fieldFacets) {
257            String facetParamKey = facet.getConfig().getParamKey();
258            if (parameters.containsKey(facetParamKey)) {
259                parameters.remove(facetParamKey);
260            }
261        }
262        // Remove selected entries from range facets
263        Collection<I_CmsSearchControllerFacetRange> rangeFacets = m_result.getController().getRangeFacets().getRangeFacetControllers();
264        for (I_CmsSearchControllerFacetRange facet : rangeFacets) {
265            String facetParamKey = facet.getConfig().getParamKey();
266            if (parameters.containsKey(facetParamKey)) {
267                parameters.remove(facetParamKey);
268            }
269        }
270        // Remove selected entries from the query facet
271        I_CmsSearchControllerFacetQuery facet = m_result.getController().getQueryFacet();
272        if (null != facet) {
273            String facetParamKey = facet.getConfig().getParamKey();
274            if (parameters.containsKey(facetParamKey)) {
275                parameters.remove(facetParamKey);
276            }
277        }
278        return new CmsSearchStateParameters(m_result, parameters);
279    }
280
281    /**
282     * @see org.opencms.jsp.search.result.I_CmsSearchStateParameters#getResetFacetState()
283     */
284    @Override
285    public Map<String, I_CmsSearchStateParameters> getResetFacetState() {
286
287        if (m_resetFacetMap == null) {
288            m_resetFacetMap = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() {
289
290                @Override
291                public Object transform(final Object facet) {
292
293                    final Map<String, String[]> parameters = new HashMap<String, String[]>(m_params);
294                    String facetParamKey = getFacetParamKey((String)facet);
295                    if ((facetParamKey != null) && parameters.containsKey(facetParamKey)) {
296                        parameters.remove(facetParamKey);
297                    }
298                    return new CmsSearchStateParameters(m_result, parameters);
299                }
300            });
301        }
302        return m_resetFacetMap;
303    }
304
305    /**
306     * @see org.opencms.jsp.search.result.I_CmsSearchStateParameters#getSetAdditionalParam()
307     */
308    public Map<String, Map<String, I_CmsSearchStateParameters>> getSetAdditionalParam() {
309
310        if (m_setAdditionalParamsMap == null) {
311            m_setAdditionalParamsMap = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() {
312
313                @Override
314                public Object transform(final Object param) {
315
316                    Map<String, I_CmsSearchStateParameters> m_additionalParamsMap = CmsCollectionsGenericWrapper.createLazyMap(
317                        new Transformer() {
318
319                            @Override
320                            public Object transform(final Object value) {
321
322                                final Map<String, String[]> parameters = new HashMap<String, String[]>(m_params);
323                                boolean validParam = m_result.getController().getCommon().getConfig().getAdditionalParameters().keySet().contains(
324                                    param);
325                                if (validParam) {
326                                    parameters.put((String)param, new String[] {(String)value});
327                                }
328                                return new CmsSearchStateParameters(m_result, parameters);
329                            }
330                        });
331                    return m_additionalParamsMap;
332                }
333            });
334        }
335        return m_setAdditionalParamsMap;
336    }
337
338    /**
339     * @see org.opencms.jsp.search.result.I_CmsSearchStateParameters#getSetPage()
340     */
341    @Override
342    public Map<String, I_CmsSearchStateParameters> getSetPage() {
343
344        if (m_paginationMap == null) {
345            m_paginationMap = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() {
346
347                @Override
348                public Object transform(final Object page) {
349
350                    final Map<String, String[]> parameters = new HashMap<String, String[]>(m_params);
351                    parameters.put(
352                        m_result.getController().getPagination().getConfig().getPageParam(),
353                        new String[] {(String)page});
354                    return new CmsSearchStateParameters(m_result, parameters);
355                }
356            });
357        }
358        return m_paginationMap;
359    }
360
361    /**
362     * @see org.opencms.jsp.search.result.I_CmsSearchStateParameters#getSetSortOption()
363     */
364    @Override
365    public Map<String, I_CmsSearchStateParameters> getSetSortOption() {
366
367        if (m_sortingMap == null) {
368            m_sortingMap = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() {
369
370                @Override
371                public Object transform(final Object sortOption) {
372
373                    final Map<String, String[]> parameters = new HashMap<String, String[]>(m_params);
374                    m_result.getController().addParametersForCurrentState(parameters);
375                    parameters.put(
376                        m_result.getController().getSorting().getConfig().getSortParam(),
377                        new String[] {(String)sortOption});
378                    return new CmsSearchStateParameters(m_result, parameters);
379                }
380            });
381        }
382        return m_sortingMap;
383    }
384
385    /**
386     * @see org.opencms.jsp.search.result.I_CmsSearchStateParameters#getUncheckFacetItem()
387     */
388    @Override
389    public Map<String, Map<String, I_CmsSearchStateParameters>> getUncheckFacetItem() {
390
391        if (m_uncheckFacetMap == null) {
392            m_uncheckFacetMap = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() {
393
394                @Override
395                public Object transform(final Object facet) {
396
397                    Map<String, I_CmsSearchStateParameters> m_uncheckEntries = CmsCollectionsGenericWrapper.createLazyMap(
398                        new Transformer() {
399
400                            @Override
401                            public Object transform(final Object facetItem) {
402
403                                final Map<String, String[]> parameters = new HashMap<String, String[]>(m_params);
404                                String facetParamKey = getFacetParamKey((String)facet);
405                                if ((facetParamKey != null) && parameters.containsKey(facetParamKey)) {
406                                    String[] values = parameters.get(facetParamKey);
407                                    List<String> valueList = Arrays.asList(values);
408                                    String item = (String)facetItem;
409                                    if (valueList.contains(facetItem)) {
410                                        String[] newValues = new String[valueList.size() - 1];
411                                        int i = 0;
412                                        for (String value : valueList) {
413                                            if (!value.equals(item)) {
414                                                newValues[i++] = value;
415                                            }
416                                        }
417                                        parameters.put(facetParamKey, newValues);
418                                    }
419                                }
420                                return new CmsSearchStateParameters(m_result, parameters);
421                            }
422                        });
423                    return m_uncheckEntries;
424                }
425            });
426        }
427        return m_uncheckFacetMap;
428    }
429
430    /**
431     * @see org.opencms.jsp.search.result.I_CmsSearchStateParameters#getUnsetAdditionalParam()
432     */
433    public Map<String, I_CmsSearchStateParameters> getUnsetAdditionalParam() {
434
435        if (m_unsetAdditionalParamsMap == null) {
436            m_unsetAdditionalParamsMap = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() {
437
438                @Override
439                public Object transform(final Object param) {
440
441                    final Map<String, String[]> parameters = new HashMap<String, String[]>(m_params);
442                    boolean validParam = m_result.getController().getCommon().getConfig().getAdditionalParameters().keySet().contains(
443                        param);
444                    if (validParam && parameters.containsKey(param)) {
445                        parameters.remove(param);
446                    }
447                    return new CmsSearchStateParameters(m_result, parameters);
448                }
449            });
450        }
451        return m_unsetAdditionalParamsMap;
452    }
453
454    /**
455     * @see java.lang.Object#toString()
456     */
457    @Override
458    public String toString() {
459
460        return paramMapToString(m_params);
461    }
462
463    /**
464     * Returns the parameter key of the facet with the given name.
465     * @param facet the facet's name.
466     * @return the parameter key for the facet.
467     */
468    String getFacetParamKey(String facet) {
469
470        I_CmsSearchControllerFacetField fieldFacet = m_result.getController().getFieldFacets().getFieldFacetController().get(
471            facet);
472        if (fieldFacet != null) {
473            return fieldFacet.getConfig().getParamKey();
474        }
475        I_CmsSearchControllerFacetRange rangeFacet = m_result.getController().getRangeFacets().getRangeFacetController().get(
476            facet);
477        if (rangeFacet != null) {
478            return rangeFacet.getConfig().getParamKey();
479        }
480        I_CmsSearchControllerFacetQuery queryFacet = m_result.getController().getQueryFacet();
481        if ((queryFacet != null) && queryFacet.getConfig().getName().equals(facet)) {
482            return queryFacet.getConfig().getParamKey();
483        }
484
485        // Facet did not exist
486        LOG.warn(Messages.get().getBundle().key(Messages.LOG_FACET_NOT_CONFIGURED_1, facet), new Throwable());
487
488        return null;
489    }
490
491}