org.opencms.search.CmsSearchParameters.java Source code

Java tutorial

Introduction

Here is the source code for org.opencms.search.CmsSearchParameters.java

Source

/*
 * This library is part of OpenCms -
 * the Open Source Content Management System
 *
 * Copyright (c) Alkacon Software GmbH (http://www.alkacon.com)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * For further information about Alkacon Software GmbH, please see the
 * company website: http://www.alkacon.com
 *
 * For further information about OpenCms, please see the
 * project website: http://www.opencms.org
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package org.opencms.search;

import org.opencms.i18n.CmsEncoder;
import org.opencms.main.CmsException;
import org.opencms.main.CmsIllegalArgumentException;
import org.opencms.main.CmsLog;
import org.opencms.main.OpenCms;
import org.opencms.search.fields.CmsSearchField;
import org.opencms.util.CmsStringUtil;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.collections.ListUtils;
import org.apache.commons.logging.Log;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;

/**
 * Contains the search parameters for a call to <code>{@link org.opencms.search.CmsSearchIndex#search(org.opencms.file.CmsObject, CmsSearchParameters)}</code>.<p>
 * 
 * Primary purpose is translation of search arguments to response parameters and from request parameters as 
 * well as support for creation of restrictions of several search query parameter sets. <p>
 * 
 * @since 6.0.0 
 */
public class CmsSearchParameters {

    /**
     * Describes a specific search field query.<p>
     */
    public static class CmsSearchFieldQuery {

        /** The field name. */
        private String m_fieldName;

        /** The occur parameter for this field. */
        private Occur m_fieldOccur;

        /** The search term list. */
        private List<String> m_searchTerms;

        /** The occur parameter used for the search term combination. */
        private Occur m_termOccur;

        /**
         * Creates a new search field query with a variable length search term list.<p>
         * 
         * @param fieldName the field name
         * @param fieldOccur the occur parameter for this field
         * @param termList the search term list
         * @param termOccur the occur parameter used for the search term combination
         */
        public CmsSearchFieldQuery(String fieldName, Occur fieldOccur, List<String> termList, Occur termOccur) {

            super();
            m_fieldName = fieldName;
            m_fieldOccur = fieldOccur;
            m_searchTerms = termList;
            m_termOccur = termOccur;
        }

        /**
         * Creates a new search field query with just a single search term.<p>
         * 
         * Please note: Since there is only one term, the ocucr parameter for the term combination is
         * not required and set to <code>null</code>.<p>
         * 
         * @param fieldName the field name
         * @param searchTerm the search term
         * @param fieldOccur the occur parameter for this field
         */
        public CmsSearchFieldQuery(String fieldName, String searchTerm, Occur fieldOccur) {

            this(fieldName, fieldOccur, Arrays.asList(searchTerm), null);
        }

        /**
         * Returns the field name.<p>
         *
         * @return the field name
         */
        public String getFieldName() {

            return m_fieldName;
        }

        /**
         * Returns the occur parameter for this field query.<p>
         *
         * @return the occur parameter for this field query
         */
        public Occur getOccur() {

            return m_fieldOccur;
        }

        /**
         * Returns the first entry from the term list.<p>
         *
         * @return the search query
         * 
         * @deprecated use {@link #getSearchTerms()} instead
         */
        @Deprecated
        public String getSearchQuery() {

            return m_searchTerms.get(0);
        }

        /**
         * Returns the search term list.<p>
         *
         * @return the search term list
         */
        public List<String> getSearchTerms() {

            return m_searchTerms;
        }

        /**
         * Returns the occur parameter used for the search term combination of this field query.<p>
         *
         * @return the occur parameter used for the search term combination of this field query
         */
        public Occur getTermOccur() {

            return m_termOccur;
        }

        /**
         * Sets the name of the field to use this query for.<p>
         *
         * @param fieldName the name of the field to use this query for
         */
        public void setFieldName(String fieldName) {

            m_fieldName = fieldName;
        }

        /**
         * Sets the occur parameter for this field query.<p>
         *
         * @param occur the occur parameter to set
         */
        public void setOccur(BooleanClause.Occur occur) {

            m_fieldOccur = occur;
        }

        /**
         * Sets the search keywords to just a single entry.<p>
         *
         * @param searchQuery the single search keyword to set
         * 
         * @deprecated use {@link #setSearchTerms(List)} instead
         */
        @Deprecated
        public void setSearchQuery(String searchQuery) {

            setSearchTerms(Arrays.asList(searchQuery));
        }

        /**
         * Sets the search terms.<p>
         * 
         * @param searchTerms the search terms to set
         */
        public void setSearchTerms(List<String> searchTerms) {

            m_searchTerms = searchTerms;
        }
    }

    /** Sort result documents by date of creation, then score. */
    public static final Sort SORT_DATE_CREATED = new Sort(
            new SortField(CmsSearchField.FIELD_DATE_CREATED, SortField.STRING, true));

    /** Sort result documents by date of last modification, then score. */
    public static final Sort SORT_DATE_LASTMODIFIED = new Sort(
            new SortField(CmsSearchField.FIELD_DATE_LASTMODIFIED, SortField.STRING, true));

    /** Default sort order (by document score). */
    public static final Sort SORT_DEFAULT = Sort.RELEVANCE;

    /** Names of the default sort options. */
    public static final String[] SORT_NAMES = { "SORT_DEFAULT", "SORT_DATE_CREATED", "SORT_DATE_LASTMODIFIED",
            "SORT_TITLE" };

    /** Sort result documents by title, then score. */
    public static final Sort SORT_TITLE = new Sort(
            new SortField[] { new SortField(CmsSearchField.FIELD_TITLE, SortField.STRING), SortField.FIELD_SCORE });

    /** The log object for this class. */
    private static final Log LOG = CmsLog.getLog(CmsSearchParameters.class);

    /** The number of displayed pages returned by getPageLinks(). */
    protected int m_displayPages;

    /** The number of matches per page. */
    protected int m_matchesPerPage;

    /** If <code>true</code>, the category count is calculated for all search results. */
    private boolean m_calculateCategories;

    /** The list of categories to limit the search to. */
    private List<String> m_categories;

    /** Indicates if all fields should be used for generating the excerpt, regardless if they have been searched or not. */
    private boolean m_excerptOnlySearchedFields;

    /** The map of individual search field queries. */
    private List<CmsSearchFieldQuery> m_fieldQueries;

    /** The list of search index fields to search in. */
    private List<String> m_fields;

    /** The index to search. */
    private CmsSearchIndex m_index;

    /** Indicates if the query part should be ignored so that only filters are used for searching. */
    private boolean m_isIgnoreQuery;

    /** The creation date the resources have to have as maximum. */
    private long m_maxDateCreated;

    /** The last modification date the resources have to have as maximum. */
    private long m_maxDateLastModified;

    /** The creation date the resources have to have as minimum. */
    private long m_minDateCreated;

    /** The last modification date the resources have to have as minimum. */
    private long m_minDateLastModified;

    /** The current result page. */
    private int m_page;

    /** The pre-parsed query. */
    private String m_parsedQuery;

    /** The search query to use. */
    private String m_query;

    /** The minimum length of the search query. */
    private int m_queryLength;

    /** The list of resource types to limit the search to. */
    private List<String> m_resourceTypes;

    /** Only resource that are sub-resource of one of the search roots are included in the search result. */
    private List<String> m_roots;

    /** The sort order for the search. */
    private Sort m_sort;

    /**
     * Creates a new search parameter instance with no search query and 
     * default values for the remaining parameters. <p>
     * 
     * Before using this search parameters for a search method 
     * <code>{@link #setQuery(String)}</code> has to be invoked. <p>
     * 
     */
    public CmsSearchParameters() {

        this("");
    }

    /**
     * Creates a new search parameter instance with the provided search query and 
     * default values for the remaining parameters. <p>
     * 
     * Only the "meta" field (combination of content and title) will be used for search. 
     * No search root restriction is chosen. 
     * No category restriction is used. 
     * No category counts are calculated for the result. 
     * Sorting is turned off. This is a simple but fast setup. <p>
     * 
     * @param query the query to search for 
     */
    public CmsSearchParameters(String query) {

        this(query, null, null, null, null, false, null);

    }

    /**
     * Creates a new search parameter instance with the provided parameter values.<p>
     * 
     * @param query the search term to search the index
     * @param fields the list of fields to search
     * @param roots only resource that are sub-resource of one of the search roots are included in the search result
     * @param categories the list of categories to limit the search to
     * @param resourceTypes the list of resource types to limit the search to
     * @param calculateCategories if <code>true</code>, the category count is calculated for all search results
     *      (use with caution, this option uses much performance)
     * @param sort the sort order for the search
     */
    public CmsSearchParameters(String query, List<String> fields, List<String> roots, List<String> categories,
            List<String> resourceTypes, boolean calculateCategories, Sort sort) {

        super();
        m_query = (query == null) ? "" : query;
        if (fields == null) {
            fields = new ArrayList<String>(2);
            fields.add(CmsSearchIndex.DOC_META_FIELDS[0]);
            fields.add(CmsSearchIndex.DOC_META_FIELDS[1]);
        }
        m_fields = fields;
        if (roots == null) {
            roots = new ArrayList<String>(2);
        }
        m_roots = roots;
        m_categories = (categories == null) ? new ArrayList<String>() : categories;
        m_resourceTypes = (resourceTypes == null) ? new ArrayList<String>() : resourceTypes;
        m_calculateCategories = calculateCategories;
        // null sort is allowed default
        m_sort = sort;
        m_page = 1;
        m_queryLength = -1;
        m_matchesPerPage = 10;
        m_displayPages = 10;
        m_isIgnoreQuery = false;

        m_minDateCreated = Long.MIN_VALUE;
        m_maxDateCreated = Long.MAX_VALUE;
        m_minDateLastModified = Long.MIN_VALUE;
        m_maxDateLastModified = Long.MAX_VALUE;
    }

    /**
     * Adds an individual query for a search field.<p>
     * 
     * If this is used, any setting made with {@link #setQuery(String)} and {@link #setFields(List)}
     * will be ignored and only the individual field search settings will be used.<p>
     *
     * @param query the query to add
     * 
     * @since 7.5.1
     */
    public void addFieldQuery(CmsSearchFieldQuery query) {

        if (m_fieldQueries == null) {
            m_fieldQueries = new ArrayList<CmsSearchFieldQuery>();
            m_fields = new ArrayList<String>();
        }
        m_fieldQueries.add(query);
        // add the used field used in the fields query to the list of fields used in the search
        if (!m_fields.contains(query.getFieldName())) {
            m_fields.add(query.getFieldName());
        }
    }

    /**
     * Adds an individual query for a search field.<p>
     * 
     * If this is used, any setting made with {@link #setQuery(String)} and {@link #setFields(List)}
     * will be ignored and only the individual field search settings will be used.<p>
     * 
     * @param fieldName the field name
     * @param searchQuery the search query
     * @param occur the occur parameter for the query in the field
     * 
     * @since 7.5.1
     */
    public void addFieldQuery(String fieldName, String searchQuery, Occur occur) {

        CmsSearchFieldQuery newQuery = new CmsSearchFieldQuery(fieldName, searchQuery, occur);
        addFieldQuery(newQuery);
    }

    /**
     * Returns whether category counts are calculated for search results or not. <p>
     * 
     * @return a boolean that tells whether category counts are calculated for search results or not
     */
    public boolean getCalculateCategories() {

        return m_calculateCategories;
    }

    /**
     * Returns the list of categories to limit the search to.<p>
     *
     * @return the list of categories to limit the search to
     */
    public List<String> getCategories() {

        return m_categories;
    }

    /**
     * Returns the maximum number of pages which should be shown.<p> 
     * 
     * @return the maximum number of pages which should be shown
     */
    public int getDisplayPages() {

        return m_displayPages;
    }

    /**
     * Returns the list of individual field queries.<p>
     * 
     * @return the list of individual field queries
     *
     * @since 7.5.1
     */
    public List<CmsSearchFieldQuery> getFieldQueries() {

        return m_fieldQueries;
    }

    /**
     * Returns the list of search index field names (Strings) to search in.<p>
     *
     * @return the list of search index field names (Strings) to search in
     */
    public List<String> getFields() {

        return m_fields;
    }

    /**
     * Get the name of the index for the search.<p>
     * 
     * @return the name of the index for the search
     */
    public String getIndex() {

        return m_index.getName();
    }

    /**
     * Gets the number of matches displayed on each page.<p>
     * 
     * @return matches per result page
     */
    public int getMatchesPerPage() {

        return m_matchesPerPage;
    }

    /**
     * Returns the maximum creation date a resource must have to be included in the search result.<p>
     *
     * @return the maximum creation date a resource must have to be included in the search result
     */
    public long getMaxDateCreated() {

        return m_maxDateCreated;
    }

    /**
     * Returns the maximum last modification date a resource must have to be included in the search result.<p>
     *
     * @return the maximum last modification date a resource must have to be included in the search result
     */
    public long getMaxDateLastModified() {

        return m_maxDateLastModified;
    }

    /**
     * Returns the minimum creation date a resource must have to be included in the search result.<p>
     *
     * @return the minimum creation date a resource must have to be included in the search result
     */
    public long getMinDateCreated() {

        return m_minDateCreated;
    }

    /**
     * Returns the minimum last modification date a resource must have to be included in the search result.<p>
     *
     * @return the minimum last modification date a resource must have to be included in the search result
     */
    public long getMinDateLastModified() {

        return m_minDateLastModified;
    }

    /**
     * Returns the parsed query.<p>
     * 
     * The parsed query is automatically set by the OpenCms search index when a query is created 
     * with either {@link #setQuery(String)} or {@link #addFieldQuery(CmsSearchFieldQuery)}.
     * The Lucene query build from the parameters is stored here and can be later used 
     * for paging through the results.<p>
     * 
     * Please note that this returns only to the query part, not the filter part of the search.<p>
     * 
     * @return the parsed query 
     */
    public String getParsedQuery() {

        return m_parsedQuery;
    }

    /**
     * Returns the search query to use.<p>
     *
     * @return the search query to use
     */
    public String getQuery() {

        return m_query;
    }

    /**
     * Gets the minimum search query length.<p>
     * 
     * @return the minimum search query length
     */
    public int getQueryLength() {

        return m_queryLength;
    }

    /**
     * Returns the list of resource types to limit the search to.<p>
     *
     * @return the list of resource types to limit the search to
     *
     * @since 7.5.1
     */
    public List<String> getResourceTypes() {

        return m_resourceTypes;
    }

    /**
     * Returns the list of strings of search roots to use.<p>
     * 
     * Only resource that are sub-resource of one of the search roots are included in the search result.<p>
     * 
     * @return the list of strings of search roots to use
     */
    public List<String> getRoots() {

        return m_roots;
    }

    /**
     * Returns the list of categories to limit the search to.<p>
     *
     * @return the list of categories to limit the search to
     */
    public String getSearchCategories() {

        return toSeparatedString(getCategories(), ',');
    }

    /**
     * Returns the search index to search in or null if not set before 
     * (<code>{@link #setSearchIndex(CmsSearchIndex)}</code>). <p>
     * 
     * @return the search index to search in or null if not set before (<code>{@link #setSearchIndex(CmsSearchIndex)}</code>)
     */
    public CmsSearchIndex getSearchIndex() {

        return m_index;
    }

    /**
     * Returns the search page to display.<p>
     *  
     * @return the search page to display
     */
    public int getSearchPage() {

        return m_page;
    }

    /**
     * Returns the comma separated lists of root folder names to restrict search to.<p>
     * 
     * This method is a "sibling" to method <code>{@link #getRoots()}</code> but with 
     * the support of being usable with widget technology. <p>
     * 
     * @return the comma separated lists of field names to search in
     * 
     * @see #setSortName(String)
     */
    public String getSearchRoots() {

        return toSeparatedString(m_roots, ',');
    }

    /**
     * Returns the instance that defines the sort order for the results. 
     * 
     * @return the instance that defines the sort order for the results
     */
    public Sort getSort() {

        return m_sort;
    }

    /**
     * Returns the name of the sort option being used.<p>
     * @return the name of the sort option being used
     * 
     * @see #SORT_NAMES
     * @see #setSortName(String)
     */
    public String getSortName() {

        if (m_sort == SORT_DATE_CREATED) {
            return SORT_NAMES[1];
        }
        if (m_sort == SORT_DATE_LASTMODIFIED) {
            return SORT_NAMES[2];
        }
        if (m_sort == SORT_TITLE) {
            return SORT_NAMES[3];
        }
        return SORT_NAMES[0];
    }

    /**
     * Returns <code>true</code> if the category count is calculated for all search results.<p>
     *
     * @return <code>true</code> if the category count is calculated for all search results
     */
    public boolean isCalculateCategories() {

        return m_calculateCategories;
    }

    /**
     * Returns <code>true</code> if fields configured for the excerpt should be used for generating the excerpt only 
     * if they have been actually searched in.<p>
     *
     * The default setting is <code>false</code>, which means all text fields configured for the excerpt will
     * be used to generate the excerpt, regardless if they have been searched in or not.<p>
     *
     * Please note: A field will only be included in the excerpt if it has been configured as <code>excerpt="true"</code>
     * in <code>opencms-search.xml</code>. This method controls if so configured fields are used depending on the
     * fields searched, see {@link #setFields(List)}.<p>
     *
     * @return <code>true</code> if fields configured for the excerpt should be used for generating the excerpt only 
     * if they have been actually searched in
     */
    public boolean isExcerptOnlySearchedFields() {

        return m_excerptOnlySearchedFields;
    }

    /**
     * Returns <code>true</code> if the query part should be ignored so that only filters are used for searching.<p>
     *
     * @return <code>true</code> if the query part should be ignored so that only filters are used for searching
     * 
     * @since 8.0.0
     */
    public boolean isIgnoreQuery() {

        return m_isIgnoreQuery;
    }

    /**
     * Creates a merged parameter set from this parameters, restricted by the given other parameters.<p>
     * 
     * This is mainly intended for "search in search result" functions.<p>
     * 
     * The restricted query is build of the queries of both parameters, appended with AND.<p>
     * 
     * The lists in the restriction for <code>{@link #getFields()}</code>, <code>{@link #getRoots()}</code> and
     * <code>{@link #getCategories()}</code> are <b>intersected</b> with the lists of this search parameters. Only
     * elements contained in both lists are included for the created search parameters. 
     * If a list in either the restriction or in this search parameters is <code>null</code>, 
     * the list from the other search parameters is used directly.<p> 
     * 
     * The values for
     * <code>{@link #isCalculateCategories()}</code>
     * and <code>{@link #getSort()}</code> of this parameters are used for the restricted parameters.<p>
     * 
     * @param restriction the parameters to restrict this parameters with
     * @return the restricted parameters
     */
    @SuppressWarnings("unchecked")
    public CmsSearchParameters restrict(CmsSearchParameters restriction) {

        // append queries
        StringBuffer query = new StringBuffer(256);
        if (getQuery() != null) {
            // don't blow up unnecessary closures (if CmsSearch is reused and restricted several times)
            boolean closure = !getQuery().startsWith("+(");
            if (closure) {
                query.append("+(");
            }
            query.append(getQuery());
            if (closure) {
                query.append(")");
            }
        }
        if (restriction.getQuery() != null) {
            // don't let Lucene max terms be exceeded in case someone reuses a CmsSearch and continuously restricts 
            // query with the same restrictions...
            if (query.indexOf(restriction.getQuery()) < 0) {
                query.append(" +(");
                query.append(restriction.getQuery());
                query.append(")");
            }
        }

        // restrict fields
        List<String> fields = null;
        if ((m_fields != null) && (m_fields.size() > 0)) {
            if ((restriction.getFields() != null) && (restriction.getFields().size() > 0)) {
                fields = ListUtils.intersection(m_fields, restriction.getFields());
            } else {
                fields = m_fields;
            }
        } else {
            fields = restriction.getFields();
        }

        // restrict roots
        List<String> roots = null;
        if ((m_roots != null) && (m_roots.size() > 0)) {
            if ((restriction.getRoots() != null) && (restriction.getRoots().size() > 0)) {
                roots = ListUtils.intersection(m_roots, restriction.getRoots());
                // TODO: This only works if there are equal paths in both parameter sets - for two distinct sets 
                //       all root restrictions are dropped with an empty list. 
            } else {
                roots = m_roots;
            }
        } else {
            roots = restriction.getRoots();
        }

        // restrict categories
        List<String> categories = null;
        if ((m_categories != null) && (m_categories.size() > 0)) {
            if ((restriction.getCategories() != null) && (restriction.getCategories().size() > 0)) {
                categories = ListUtils.intersection(m_categories, restriction.getCategories());
            } else {
                categories = m_categories;
            }
        } else {
            categories = restriction.getCategories();
        }

        // restrict resource types
        List<String> resourceTypes = null;
        if ((m_resourceTypes != null) && (m_resourceTypes.size() > 0)) {
            if ((restriction.getResourceTypes() != null) && (restriction.getResourceTypes().size() > 0)) {
                resourceTypes = ListUtils.intersection(m_resourceTypes, restriction.getResourceTypes());
            } else {
                resourceTypes = m_resourceTypes;
            }
        } else {
            resourceTypes = restriction.getResourceTypes();
        }

        // create the new search parameters 
        CmsSearchParameters result = new CmsSearchParameters(query.toString(), fields, roots, categories,
                resourceTypes, m_calculateCategories, m_sort);
        result.setIndex(getIndex());
        return result;
    }

    /**
     * Set whether category counts shall be calculated for the corresponding search results or not.<p> 
     * 
     * @param flag true if category counts shall be calculated for the corresponding search results or false if not
     */
    public void setCalculateCategories(boolean flag) {

        m_calculateCategories = flag;
    }

    /**
     * Set the list of categories (strings) to this parameters. <p> 
     * 
     * @param categories the list of categories (strings) of this parameters
     */
    public void setCategories(List<String> categories) {

        m_categories = categories;
    }

    /**
     * Sets the maximum number of pages which should be shown.<p>
     * 
     * Enter an odd value to achieve a nice, "symmetric" output.<p> 
     * 
     * @param value the maximum number of pages which should be shown
     */
    public void setDisplayPages(int value) {

        m_displayPages = value;
    }

    /**
     * Controls if the excerpt from a field is generated only for searched fields, or for all fields (the default).<p>
     *
     * @param excerptOnlySearchedFields if <code>true</code>, the excerpt is generated only from the fields actually searched in
     * 
     * @see #isExcerptOnlySearchedFields()
     */
    public void setExcerptOnlySearchedFields(boolean excerptOnlySearchedFields) {

        m_excerptOnlySearchedFields = excerptOnlySearchedFields;
    }

    /**
     * Sets the list of strings of names of fields to search in. <p>
     * 
     * @param fields the list of strings of names of fields to search in to set
     */
    public void setFields(List<String> fields) {

        m_fields = fields;
    }

    /**
     * Sets the flag to indicate if the query part should be ignored so that only filters are used for searching.<p>
     *
     * @param isIgnoreQuery the flag to indicate if the query part should be ignored
     * 
     * @since 8.0.0
     */
    public void setIgnoreQuery(boolean isIgnoreQuery) {

        m_isIgnoreQuery = isIgnoreQuery;
    }

    /**
     * Set the name of the index to search.<p>
     * 
     * 
     * @param indexName the name of the index
     */
    public void setIndex(String indexName) {

        CmsSearchIndex index;
        if (CmsStringUtil.isNotEmpty(indexName)) {
            try {
                index = OpenCms.getSearchManager().getIndex(indexName);
                if (index == null) {
                    throw new CmsException(Messages.get().container(Messages.ERR_INDEX_NOT_FOUND_1, indexName));
                }
                setSearchIndex(index);
            } catch (Exception exc) {
                if (LOG.isErrorEnabled()) {
                    LOG.error(Messages.get().getBundle().key(Messages.LOG_INDEX_ACCESS_FAILED_1, indexName), exc);
                }
            }
        }
    }

    /**
     * Sets the number of matches per page.<p>
     * 
     * @param matches the number of matches per page
     */
    public void setMatchesPerPage(int matches) {

        m_matchesPerPage = matches;
    }

    /**
     * Sets the maximum creation date a resource must have to be included in the search result.<p>
     *
     * @param maxDateCreated the maximum creation date to set
     */
    public void setMaxDateCreated(long maxDateCreated) {

        m_maxDateCreated = maxDateCreated;
    }

    /**
     * Sets the maximum last modification date a resource must have to be included in the search result.<p>
     *
     * @param maxDateLastModified the maximum last modification date to set
     */
    public void setMaxDateLastModified(long maxDateLastModified) {

        m_maxDateLastModified = maxDateLastModified;
    }

    /**
     * Sets the minimum creation date a resource must have to be included in the search result.<p>
     *
     * @param minDateCreated the minimum creation date to set
     */
    public void setMinDateCreated(long minDateCreated) {

        m_minDateCreated = minDateCreated;
    }

    /**
     * Sets the minimum last modification date a resource must have to be included in the search result.<p>
     *
     * @param minDateLastModified he minimum last modification date to set
     */
    public void setMinDateLastModified(long minDateLastModified) {

        m_minDateLastModified = minDateLastModified;
    }

    /**
     * Sets the parsed query.<p>
     * 
     * The parsed query is automatically set by the OpenCms search index when a query is created 
     * with either {@link #setQuery(String)} or {@link #addFieldQuery(CmsSearchFieldQuery)}.
     * The Lucene query build from the parameters is stored here and can be later used 
     * for paging through the results.<p>
     * 
     * Please note that this applies only to the query part, not the filter part of the search.<p>
     * 
     * @param parsedQuery the parsed query to set 
     */
    public void setParsedQuery(String parsedQuery) {

        m_parsedQuery = parsedQuery;
    }

    /**
     * Sets the query to search for. <p> 
     * 
     * The decoding here is tailored for query strings that are 
     * additionally manually UTF-8 encoded at client side (javascript) to get around an 
     * issue with special chars in applications that use non- UTF-8 encoding 
     * (e.g. ISO-8859-1) OpenCms applications. It is not recommended to use this with 
     * front ends that don't encode manually as characters like sole "%" (without number suffix) 
     * will cause an Exception.<p> 
     * 
     * @param query the query to search for to set
     */
    public void setQuery(String query) {

        // for use with widgets the exception is thrown here to enforce the error message next to the widget
        if (query.trim().length() < getQueryLength()) {
            throw new CmsIllegalArgumentException(
                    Messages.get().container(Messages.ERR_QUERY_TOO_SHORT_1, new Integer(getQueryLength())));
        }
        m_query = query;
    }

    /**
     * Sets the minimum length of the search query.<p>
     * 
     * @param length the minimum search query length
     */
    public void setQueryLength(int length) {

        m_queryLength = length;
    }

    /**
     * Set the list of resource types (strings) to limit the search to. <p> 
     * 
     * @param resourceTypes the list of resource types (strings) to limit the search to
     *
     * @since 7.5.1
     */
    public void setResourceTypes(List<String> resourceTypes) {

        m_resourceTypes = resourceTypes;
    }

    /**
     * Sets the list of strings of roots to search under for the search.<p>
     * 
     * @param roots  the list of strings of roots to search under for the search to set
     */
    public void setRoots(List<String> roots) {

        m_roots = roots;
    }

    /**
     * Set the comma separated search root names to  restrict search to.<p>
     * 
     * @param categories the comma separated category names to  restrict search to
     */
    public void setSearchCategories(String categories) {

        setCategories(CmsStringUtil.splitAsList(categories, ','));
    }

    /**
     * Sets the search index to use for the search. <p>
     * 
     * @param index the search index to use for the search to set.
     * 
     * @throws CmsIllegalArgumentException if null is given as argument 
     */
    public void setSearchIndex(CmsSearchIndex index) throws CmsIllegalArgumentException {

        if (index == null) {
            throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_INDEX_NULL_0));
        }
        m_index = index;
    }

    /**
     * Set the search page to display. <p>
     * 
     * @param page the search page to display
     */
    public void setSearchPage(int page) {

        m_page = page;
    }

    /**
     * Set the comma separated search root names to  restrict search to.<p>
     * 
     * @param rootNameList the comma separated search root names to  restrict search to
     */
    public void setSearchRoots(String rootNameList) {

        m_roots = CmsStringUtil.splitAsList(rootNameList, ',');
    }

    /**
     * Set the instance that defines the sort order for search results. 
     * 
     * @param sortOrder the instance that defines the sort order for search results to set
     */
    public void setSort(Sort sortOrder) {

        m_sort = sortOrder;
    }

    /** 
     * Sets the internal member of type <code>{@link Sort}</code> according to 
     * the given sort name. <p>
     * 
     * For a list of valid sort names, please see <code>{@link #SORT_NAMES}</code>.<p>
     * 
     * @param sortName the name of the sort to use
     * 
     * @see #SORT_NAMES
     */
    public void setSortName(String sortName) {

        if (sortName.equals(SORT_NAMES[1])) {
            m_sort = SORT_DATE_CREATED;
        } else if (sortName.equals(SORT_NAMES[2])) {
            m_sort = SORT_DATE_LASTMODIFIED;
        } else if (sortName.equals(SORT_NAMES[3])) {
            m_sort = SORT_TITLE;
        } else {
            m_sort = SORT_DEFAULT;
        }
    }

    /**
     * Creates a query String build from this search parameters for HTML links.<p>
     * 
     * @return a query String build from this search parameters for HTML links
     */
    public String toQueryString() {

        StringBuffer result = new StringBuffer(128);
        result.append("?action=search");
        if (getParsedQuery() != null) {
            result.append("&parsedQuery=");
            result.append(CmsEncoder.encodeParameter(getParsedQuery()));
        } else {
            result.append("&query=");
            result.append(CmsEncoder.encodeParameter(getQuery()));
        }

        result.append("&matchesPerPage=");
        result.append(getMatchesPerPage());
        result.append("&displayPages=");
        result.append(getDisplayPages());
        result.append("&index=");
        result.append(CmsEncoder.encodeParameter(getIndex()));

        Sort sort = getSort();
        if (sort != CmsSearchParameters.SORT_DEFAULT) {
            result.append("&sort=");
            if (sort == CmsSearchParameters.SORT_TITLE) {
                result.append("title");
            } else if (sort == CmsSearchParameters.SORT_DATE_CREATED) {
                result.append("date-created");
            } else if (sort == CmsSearchParameters.SORT_DATE_LASTMODIFIED) {
                result.append("date-lastmodified");
            }
        }

        if ((getCategories() != null) && (getCategories().size() > 0)) {
            result.append("&category=");
            Iterator<String> it = getCategories().iterator();
            while (it.hasNext()) {
                result.append(it.next());
                if (it.hasNext()) {
                    result.append(',');
                }
            }
        }

        if (getMinDateCreated() > Long.MIN_VALUE) {
            result.append("&minDateCreated=");
            result.append(getMinDateCreated());
        }
        if (getMinDateLastModified() > Long.MIN_VALUE) {
            result.append("&minDateLastModified=");
            result.append(getMinDateLastModified());
        }
        if (getMaxDateCreated() < Long.MAX_VALUE) {
            result.append("&maxDateCreated=");
            result.append(getMaxDateCreated());
        }
        if (getMaxDateLastModified() < Long.MAX_VALUE) {
            result.append("&maxDateLastModified=");
            result.append(getMaxDateLastModified());
        }

        if ((getRoots() != null) && (getRoots().size() > 0)) {
            result.append("&searchRoots=");
            Iterator<String> it = getRoots().iterator();
            while (it.hasNext()) {
                result.append(CmsEncoder.encode(it.next()));
                if (it.hasNext()) {
                    result.append(',');
                }
            }
        }

        if (isExcerptOnlySearchedFields()) {
            result.append("&excerptOnlySearchedFields=true");
        }

        return result.toString();
    }

    /**
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {

        StringBuffer result = new StringBuffer();
        result.append("query:[");
        result.append(m_query);
        result.append("] ");
        if ((m_fields != null) && (m_fields.size() > 0)) {
            result.append("fields:[");
            for (int i = 0; i < m_fields.size(); i++) {
                result.append(m_fields.get(i));
                if ((i + 1) < m_fields.size()) {
                    result.append(", ");
                }
            }
            result.append("] ");
        }
        if ((m_roots != null) && (m_roots.size() > 0)) {
            result.append("roots:[");
            for (int i = 0; i < m_roots.size(); i++) {
                result.append(m_roots.get(i));
                if ((i + 1) < m_roots.size()) {
                    result.append(", ");
                }
            }
            result.append("] ");
        }
        if ((m_categories != null) && (m_categories.size() > 0)) {
            result.append("categories:[");
            for (int i = 0; i < m_categories.size(); i++) {
                result.append(m_categories.get(i));
                if ((i + 1) < m_categories.size()) {
                    result.append(", ");
                }
            }
            result.append("] ");
        }
        if ((m_resourceTypes != null) && (m_resourceTypes.size() > 0)) {
            result.append("resourceTypes:[");
            for (int i = 0; i < m_resourceTypes.size(); i++) {
                result.append(m_resourceTypes.get(i));
                if ((i + 1) < m_resourceTypes.size()) {
                    result.append(", ");
                }
            }
            result.append("] ");
        }
        if (m_calculateCategories) {
            result.append("calculate-categories ");
        }
        if (m_excerptOnlySearchedFields) {
            result.append("excerpt-searched-fields-only ");
        }
        result.append("sort:[");
        if (m_sort == CmsSearchParameters.SORT_DEFAULT) {
            result.append("default");
        } else if (m_sort == CmsSearchParameters.SORT_TITLE) {
            result.append("title");
        } else if (m_sort == CmsSearchParameters.SORT_DATE_CREATED) {
            result.append("date-created");
        } else if (m_sort == CmsSearchParameters.SORT_DATE_LASTMODIFIED) {
            result.append("date-lastmodified");
        } else {
            result.append("unknown");
        }
        result.append("]");

        return result.toString();
    }

    /**
     * Concatenates the elements of the string list separated by the given separator character.<p>
     * 
     * @param stringList the list
     * @param separator the separator
     * 
     * @return the concatenated string
     */
    private String toSeparatedString(List<String> stringList, char separator) {

        StringBuffer result = new StringBuffer();
        Iterator<String> it = stringList.iterator();
        while (it.hasNext()) {
            result.append(it.next());
            if (it.hasNext()) {
                result.append(separator);
            }
        }
        return result.toString();
    }
}