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 GmbH & Co. KG, 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.configuration;
029
030import org.opencms.i18n.CmsLocaleComparator;
031import org.opencms.main.CmsLog;
032import org.opencms.main.OpenCms;
033import org.opencms.search.CmsSearchAnalyzer;
034import org.opencms.search.CmsSearchDocumentType;
035import org.opencms.search.CmsSearchIndex;
036import org.opencms.search.CmsSearchIndexSource;
037import org.opencms.search.CmsSearchManager;
038import org.opencms.search.I_CmsSearchIndex;
039import org.opencms.search.fields.CmsLuceneField;
040import org.opencms.search.fields.CmsLuceneFieldConfiguration;
041import org.opencms.search.fields.CmsSearchField;
042import org.opencms.search.fields.CmsSearchFieldMapping;
043import org.opencms.search.fields.CmsSearchFieldMappingType;
044import org.opencms.search.fields.I_CmsSearchFieldConfiguration;
045import org.opencms.search.fields.I_CmsSearchFieldMapping;
046import org.opencms.search.solr.CmsSolrConfiguration;
047import org.opencms.util.CmsStringUtil;
048
049import java.util.ArrayList;
050import java.util.Collections;
051import java.util.Iterator;
052import java.util.Locale;
053import java.util.Map.Entry;
054
055import org.apache.commons.digester3.Digester;
056
057import org.dom4j.Element;
058
059/**
060 * Lucene search configuration class.<p>
061 *
062 * @since 6.0.0
063 */
064public class CmsSearchConfiguration extends A_CmsXmlConfiguration {
065
066    /** The "analyzer" attribute. */
067    public static final String A_ANALYZER = "analyzer";
068
069    /** The "boost" attribute. */
070    public static final String A_BOOST = "boost";
071
072    /** The "displayName" attribute. */
073    public static final String A_DISPLAY = "display";
074
075    /** The "excerpt" attribute. */
076    public static final String A_EXCERPT = "excerpt";
077
078    /** The "index" attribute. */
079    public static final String A_INDEX = "index";
080
081    /** The Solr server URL attribute, set if embedded = false. */
082    public static final String A_SERVER_URL = "serverUrl";
083
084    /** The "store" attribute. */
085    public static final String A_STORE = "store";
086
087    /** The name of the DTD for this configuration. */
088    public static final String CONFIGURATION_DTD_NAME = "opencms-search.dtd";
089
090    /** The name of the default XML file for this configuration. */
091    public static final String DEFAULT_XML_FILE_NAME = "opencms-search.xml";
092
093    /** Node name constant. */
094    public static final String N_ANALYZER = "analyzer";
095
096    /** Node name constant. */
097    public static final String N_ANALYZERS = "analyzers";
098
099    /** Node name constant. */
100    public static final String N_CLASS = "class";
101
102    /** Node name constant. */
103    public static final String N_COMMIT_MS = "commitWithinMs";
104
105    /** Node name constant. */
106    public static final String N_CONFIG_FILE = "configfile";
107
108    /** Node name constant. */
109    public static final String N_CONFIGURATION = "configuration";
110
111    /** Node name constant. */
112    public static final String N_DESCRIPTION = "description";
113
114    /** Node name constant. */
115    public static final String N_DIRECTORY = "directory";
116
117    /** Node name constant. */
118    public static final String N_DOCUMENTTYPE = "documenttype";
119
120    /** Node name constant. */
121    public static final String N_DOCUMENTTYPES = "documenttypes";
122
123    /** Node name constant. */
124    public static final String N_DOCUMENTTYPES_INDEXED = "documenttypes-indexed";
125
126    /** Node name constant. */
127    public static final String N_EXCERPT = "excerpt";
128
129    /** Node name constant. */
130    public static final String N_EXTRACTION_CACHE_MAX_AGE = "extractionCacheMaxAge";
131
132    /** Node name constant. */
133    public static final String N_FIELD = "field";
134
135    /** Node name constant. */
136    public static final String N_FIELDCONFIGURATION = "fieldconfiguration";
137
138    /** Node name constant. */
139    public static final String N_FIELDCONFIGURATIONS = "fieldconfigurations";
140
141    /** Node name constant. */
142    public static final String N_FIELDS = "fields";
143
144    /** Node name constant. */
145    public static final String N_FORCEUNLOCK = "forceunlock";
146
147    /** Node name constant. */
148    public static final String N_HIGHLIGHTER = "highlighter";
149
150    /** Node name constant. */
151    public static final String N_HOME = "home";
152
153    /** Node name constant. */
154    public static final String N_INDEX = "index";
155
156    /** Node name constant. */
157    public static final String N_INDEXER = "indexer";
158
159    /** Node name constant. */
160    public static final String N_INDEXES = "indexes";
161
162    /** Node name constant. */
163    public static final String N_INDEXSOURCE = "indexsource";
164
165    /** Node name constant. */
166    public static final String N_INDEXSOURCES = "indexsources";
167
168    /** Node name constant. */
169
170    /** Node name constant. */
171    public static final String N_LOCALE = "locale";
172
173    /** Node name constant. */
174    public static final String N_MAPPING = "mapping";
175
176    /** Node name constant. */
177    public static final String N_MAX_MODIFICATIONS_BEFORE_COMMIT = "maxModificationsBeforeCommit";
178
179    /** Node name constant. */
180    public static final String N_MIMETYPE = "mimetype";
181
182    /** Node name constant. */
183    public static final String N_MIMETYPES = "mimetypes";
184
185    /** Node name constant. */
186    public static final String N_OFFLINE_UPDATE_FREQUENCY = "offlineUpdateFrequency";
187
188    /** Node name constant. */
189    public static final String N_MAX_INDEX_WAIT_TIME = "maxIndexWaitTime";
190
191    /** Node name constant. */
192    public static final String N_PROJECT = "project";
193
194    /** Node name constant. */
195    public static final String N_REBUILD = "rebuild";
196
197    /** Node name constant. */
198    public static final String N_RESOURCES = "resources";
199
200    /** Node name constant. */
201    public static final String N_RESOURCETYPE = "resourcetype";
202
203    /** Node name constant. */
204    public static final String N_RESOURCETYPES = "resourcetypes";
205
206    /** Node name constant. */
207    public static final String N_SEARCH = "search";
208
209    /** Node name constant. */
210    public static final String N_SOLR = "solr";
211
212    /** Node name constant. */
213    public static final String N_SOURCE = "source";
214
215    /** Node name constant. */
216    public static final String N_SOURCES = "sources";
217
218    /** Node name constant. */
219    public static final String N_STEMMER = "stemmer";
220
221    /** Node name constant. */
222    public static final String N_TIMEOUT = "timeout";
223
224    /** Node name constant. */
225    private static final String XPATH_SEARCH = "*/" + N_SEARCH;
226
227    /** Node name constant. */
228    private static final String N_MAX_PROCESSED_RESULTS = "maxProcessedResults";
229
230    /** The configured search manager. */
231    private CmsSearchManager m_searchManager;
232
233    /**
234     * @see org.opencms.configuration.I_CmsXmlConfiguration#addXmlDigesterRules(org.apache.commons.digester3.Digester)
235     */
236    public void addXmlDigesterRules(Digester digester) {
237
238        String xPath = null;
239
240        // add finish rule
241        digester.addCallMethod(XPATH_SEARCH, "initializeFinished");
242
243        // creation of the search manager
244        digester.addObjectCreate(XPATH_SEARCH, CmsSearchManager.class.getName(), A_CLASS);
245
246        // search manager finished
247        digester.addSetNext(XPATH_SEARCH, "setSearchManager");
248
249        // directory rule
250        digester.addCallMethod(XPATH_SEARCH + "/" + N_DIRECTORY, "setDirectory", 0);
251
252        // timeout rule
253        digester.addCallMethod(XPATH_SEARCH + "/" + N_TIMEOUT, "setTimeout", 0);
254
255        // offline update rule
256        digester.addCallMethod(XPATH_SEARCH + "/" + N_OFFLINE_UPDATE_FREQUENCY, "setOfflineUpdateFrequency", 0);
257
258        // offline update rule
259        digester.addCallMethod(XPATH_SEARCH + "/" + N_MAX_INDEX_WAIT_TIME, "setMaxIndexWaitTime", 0);
260
261        // forceunlock rule
262        digester.addCallMethod(XPATH_SEARCH + "/" + N_FORCEUNLOCK, "setForceunlock", 0);
263
264        // rule for the max. char. lenght of the search result excerpt
265        digester.addCallMethod(XPATH_SEARCH + "/" + N_EXCERPT, "setMaxExcerptLength", 0);
266
267        // rule for the max. age of entries in the extraction cache
268        digester.addCallMethod(XPATH_SEARCH + "/" + N_EXTRACTION_CACHE_MAX_AGE, "setExtractionCacheMaxAge", 0);
269
270        // rule for max. number of modifications before commit
271        digester.addCallMethod(
272            XPATH_SEARCH + "/" + N_MAX_MODIFICATIONS_BEFORE_COMMIT,
273            "setMaxModificationsBeforeCommit",
274            0);
275
276        // rule for the highlighter to highlight the search terms in the excerpt of the search result
277        digester.addCallMethod(XPATH_SEARCH + "/" + N_HIGHLIGHTER, "setHighlighter", 0);
278
279        xPath = XPATH_SEARCH + "/" + N_SOLR;
280        digester.addObjectCreate(xPath, CmsSolrConfiguration.class);
281        digester.addCallMethod(xPath, "setEnabled", 1);
282        digester.addCallParam(xPath, 0, A_ENABLED);
283        digester.addCallMethod(xPath, "setServerUrl", 1);
284        digester.addCallParam(xPath, 0, A_SERVER_URL);
285        digester.addCallMethod(xPath + "/" + N_HOME, "setHomeFolderPath", 0);
286        digester.addCallMethod(xPath + "/" + N_CONFIG_FILE, "setSolrFileName", 0);
287        digester.addCallMethod(xPath + "/" + N_COMMIT_MS, "setSolrCommitMs", 0);
288        digester.addCallMethod(xPath + "/" + N_MAX_PROCESSED_RESULTS, "setMaxProcessedResults", 0);
289        digester.addSetNext(xPath, "setSolrServerConfiguration");
290
291        // document type rule
292        xPath = XPATH_SEARCH + "/" + N_DOCUMENTTYPES + "/" + N_DOCUMENTTYPE;
293        digester.addObjectCreate(xPath, CmsSearchDocumentType.class);
294        digester.addCallMethod(xPath + "/" + N_NAME, "setName", 0);
295        digester.addCallMethod(xPath + "/" + N_CLASS, "setClassName", 0);
296        digester.addCallMethod(xPath + "/" + N_MIMETYPES + "/" + N_MIMETYPE, "addMimeType", 0);
297        digester.addCallMethod(xPath + "/" + N_RESOURCETYPES + "/" + N_RESOURCETYPE, "addResourceType", 0);
298        digester.addSetNext(xPath, "addDocumentTypeConfig");
299
300        // analyzer rule
301        xPath = XPATH_SEARCH + "/" + N_ANALYZERS + "/" + N_ANALYZER;
302        digester.addObjectCreate(xPath, CmsSearchAnalyzer.class);
303        digester.addCallMethod(xPath + "/" + N_CLASS, "setClassName", 0);
304        digester.addCallMethod(xPath + "/" + N_STEMMER, "setStemmerAlgorithm", 0);
305        digester.addCallMethod(xPath + "/" + N_LOCALE, "setLocaleString", 0);
306        digester.addSetNext(xPath, "addAnalyzer");
307
308        // search index rule
309        xPath = XPATH_SEARCH + "/" + N_INDEXES + "/" + N_INDEX;
310        digester.addObjectCreate(xPath, CmsSearchIndex.class.getName(), A_CLASS);
311        digester.addCallMethod(xPath + "/" + N_NAME, "setName", 0);
312        digester.addCallMethod(xPath + "/" + N_REBUILD, "setRebuildMode", 0);
313        digester.addCallMethod(xPath + "/" + N_PROJECT, "setProject", 0);
314        digester.addCallMethod(xPath + "/" + N_LOCALE, "setLocaleString", 0);
315        digester.addCallMethod(xPath + "/" + N_CONFIGURATION, "setFieldConfigurationName", 0);
316        digester.addCallMethod(xPath + "/" + N_SOURCES + "/" + N_SOURCE, "addSourceName", 0);
317        digester.addSetNext(xPath, "addSearchIndex");
318
319        // search index source rule
320        xPath = XPATH_SEARCH + "/" + N_INDEXSOURCES + "/" + N_INDEXSOURCE;
321        digester.addObjectCreate(xPath, CmsSearchIndexSource.class);
322        digester.addCallMethod(xPath + "/" + N_NAME, "setName", 0);
323        digester.addCallMethod(xPath + "/" + N_INDEXER, "setIndexerClassName", 1);
324        digester.addCallParam(xPath + "/" + N_INDEXER, 0, N_CLASS);
325        digester.addCallMethod(xPath + "/" + N_RESOURCES + "/" + N_RESOURCE, "addResourceName", 0);
326        digester.addCallMethod(xPath + "/" + N_DOCUMENTTYPES_INDEXED + "/" + N_NAME, "addDocumentType", 0);
327        digester.addSetNext(xPath, "addSearchIndexSource");
328
329        // field configuration rules
330        xPath = XPATH_SEARCH + "/" + N_FIELDCONFIGURATIONS + "/" + N_FIELDCONFIGURATION;
331        digester.addObjectCreate(xPath, CmsLuceneFieldConfiguration.class.getName(), A_CLASS);
332        digester.addCallMethod(xPath + "/" + N_NAME, "setName", 0);
333        digester.addCallMethod(xPath + "/" + N_DESCRIPTION, "setDescription", 0);
334        digester.addSetNext(xPath, "addFieldConfiguration");
335
336        xPath = xPath + "/" + N_FIELDS + "/" + N_FIELD;
337        digester.addObjectCreate(xPath, CmsLuceneField.class);
338        // Node: It is important to have that rule first, since then it is called last
339        // at the closing part of the XML node where rules are executed in reverse order.
340        // Thus, use this order to have the field already completely initialized when added
341        // to the configuration
342        digester.addSetNext(xPath, "addField");
343        digester.addCallMethod(xPath, "setName", 1);
344        digester.addCallParam(xPath, 0, I_CmsXmlConfiguration.A_NAME);
345        digester.addCallMethod(xPath, "setDisplayNameForConfiguration", 1);
346        digester.addCallParam(xPath, 0, A_DISPLAY);
347        digester.addCallMethod(xPath, "setStored", 1);
348        digester.addCallParam(xPath, 0, A_STORE);
349        digester.addCallMethod(xPath, "setIndexed", 1);
350        digester.addCallParam(xPath, 0, A_INDEX);
351        digester.addCallMethod(xPath, "setInExcerpt", 1);
352        digester.addCallParam(xPath, 0, A_EXCERPT);
353        digester.addCallMethod(xPath, "setAnalyzer", 1);
354        digester.addCallParam(xPath, 0, A_ANALYZER);
355        digester.addCallMethod(xPath, "setBoost", 1);
356        digester.addCallParam(xPath, 0, A_BOOST);
357        digester.addCallMethod(xPath, "setDefaultValue", 1);
358        digester.addCallParam(xPath, 0, A_DEFAULT);
359        digester.addCallMethod(xPath, "setType", 1);
360        digester.addCallParam(xPath, 0, A_TYPE);
361
362        xPath = xPath + "/" + N_MAPPING;
363        digester.addObjectCreate(xPath, CmsSearchFieldMapping.class.getName(), A_CLASS);
364        digester.addCallMethod(xPath, "setDefaultValue", 1);
365        digester.addCallParam(xPath, 0, A_DEFAULT);
366        digester.addCallMethod(xPath, "setType", 1);
367        digester.addCallParam(xPath, 0, A_TYPE);
368        digester.addCallMethod(xPath, "setParam", 0);
369        digester.addSetNext(xPath, "addMapping");
370
371        // generic <param> parameter rules
372        digester.addCallMethod(
373            "*/" + I_CmsXmlConfiguration.N_PARAM,
374            I_CmsConfigurationParameterHandler.ADD_PARAMETER_METHOD,
375            2);
376        digester.addCallParam("*/" + I_CmsXmlConfiguration.N_PARAM, 0, I_CmsXmlConfiguration.A_NAME);
377        digester.addCallParam("*/" + I_CmsXmlConfiguration.N_PARAM, 1);
378    }
379
380    /**
381     * @see org.opencms.configuration.I_CmsXmlConfiguration#generateXml(org.dom4j.Element)
382     */
383    public Element generateXml(Element parent) {
384
385        // add <search> node
386        Element searchElement = parent.addElement(N_SEARCH);
387        if (OpenCms.getRunLevel() >= OpenCms.RUNLEVEL_3_SHELL_ACCESS) {
388            // initialized OpenCms instance is available, use latest values
389            m_searchManager = OpenCms.getSearchManager();
390        }
391
392        // add class attribute (if required)
393        if (!m_searchManager.getClass().equals(CmsSearchManager.class)) {
394            searchElement.addAttribute(A_CLASS, m_searchManager.getClass().getName());
395        }
396
397        // add the Solr node
398        if (m_searchManager.getSolrServerConfiguration() != null) {
399            Element solr = searchElement.addElement(N_SOLR);
400            CmsSolrConfiguration conf = m_searchManager.getSolrServerConfiguration();
401            solr.addAttribute(A_ENABLED, Boolean.valueOf(conf.isEnabled()).toString());
402            if (conf.getServerUrl() != null) {
403                solr.addAttribute(A_SERVER_URL, conf.getServerUrl().toString());
404            }
405            if (conf.getHomeFolderPath() != null) {
406                solr.addElement(N_HOME).addText(conf.getHomeFolderPath());
407            }
408            if (conf.getSolrFileName() != null) {
409                solr.addElement(N_CONFIG_FILE).addText(conf.getSolrFileName());
410            }
411            solr.addElement(N_COMMIT_MS).addText(String.valueOf(conf.getSolrCommitMs()));
412        }
413
414        // add <directory> element
415        searchElement.addElement(N_DIRECTORY).addText(m_searchManager.getDirectory());
416        // add <timeout> element
417        searchElement.addElement(N_TIMEOUT).addText(String.valueOf(m_searchManager.getTimeout()));
418        //add <offlineUpdateFrequency> element
419        searchElement.addElement(N_OFFLINE_UPDATE_FREQUENCY).addText(
420            String.valueOf(m_searchManager.getOfflineUpdateFrequency()));
421        //add <maxIndexWaitTime> element
422        searchElement.addElement(N_MAX_INDEX_WAIT_TIME).addText(String.valueOf(m_searchManager.getMaxIndexWaitTime()));
423        // add <forceunlock> element
424        if (m_searchManager.getForceunlock() != null) {
425            searchElement.addElement(N_FORCEUNLOCK).addText(m_searchManager.getForceunlock().toString());
426        }
427        // add <exerpt> element
428        searchElement.addElement(N_EXCERPT).addText(String.valueOf(m_searchManager.getMaxExcerptLength()));
429        // add <extractionCacheMaxAge> element
430        searchElement.addElement(N_EXTRACTION_CACHE_MAX_AGE).addText(
431            String.valueOf(m_searchManager.getExtractionCacheMaxAge()));
432        // add <maxModificationsBeforeCommit> element
433        searchElement.addElement(N_MAX_MODIFICATIONS_BEFORE_COMMIT).addText(
434            String.valueOf(m_searchManager.getMaxModificationsBeforeCommit()));
435        // add <highlighter> element
436        searchElement.addElement(N_HIGHLIGHTER).addText(m_searchManager.getHighlighter().getClass().getName());
437
438        // <documenttypes>
439        Element documenttypesElement = searchElement.addElement(N_DOCUMENTTYPES);
440        for (CmsSearchDocumentType currSearchDocType : m_searchManager.getDocumentTypeConfigs()) {
441            // add the next <documenttype> element
442            Element documenttypeElement = documenttypesElement.addElement(N_DOCUMENTTYPE);
443            // add <name> element
444            documenttypeElement.addElement(N_NAME).addText(currSearchDocType.getName());
445            // add <class> element
446            documenttypeElement.addElement(N_CLASS).addText(currSearchDocType.getClassName());
447            // add <mimetypes> element
448            Element mimetypesElement = documenttypeElement.addElement(N_MIMETYPES);
449            // get the list of mimetypes to trigger the document factory class
450            Iterator<String> mimeTypesIterator = currSearchDocType.getMimeTypes().iterator();
451            while (mimeTypesIterator.hasNext()) {
452                // add <mimetype> element(s)
453                mimetypesElement.addElement(N_MIMETYPE).addText(mimeTypesIterator.next());
454            }
455            // add <resourcetypes> element
456            Element restypesElement = documenttypeElement.addElement(N_RESOURCETYPES);
457            // get the list of Cms resource types to trigger the document factory
458            Iterator<String> resTypesIterator = currSearchDocType.getResourceTypes().iterator();
459            while (resTypesIterator.hasNext()) {
460                // add <resourcetype> element(s)
461                restypesElement.addElement(N_RESOURCETYPE).addText(resTypesIterator.next());
462            }
463        }
464        // </documenttypes>
465
466        // <analyzers>
467        Element analyzersElement = searchElement.addElement(N_ANALYZERS);
468        ArrayList<Locale> analyzerLocaleList = new ArrayList<Locale>(m_searchManager.getAnalyzers().keySet());
469        // sort Analyzers in ascending order
470        Collections.sort(analyzerLocaleList, CmsLocaleComparator.getComparator());
471        Iterator<Locale> analyzersLocaleInterator = analyzerLocaleList.iterator();
472        while (analyzersLocaleInterator.hasNext()) {
473            CmsSearchAnalyzer searchAnalyzer = m_searchManager.getCmsSearchAnalyzer(analyzersLocaleInterator.next());
474            // add the next <analyzer> element
475            Element analyzerElement = analyzersElement.addElement(N_ANALYZER);
476            // add <class> element
477            analyzerElement.addElement(N_CLASS).addText(searchAnalyzer.getClassName());
478            // add <locale> element
479            analyzerElement.addElement(N_LOCALE).addText(searchAnalyzer.getLocale().toString());
480        }
481        // </analyzers>
482
483        // <indexes>
484        Element indexesElement = searchElement.addElement(N_INDEXES);
485        for (I_CmsSearchIndex searchIndex : m_searchManager.getSearchIndexesAll()) {
486            // add the next <index> element
487            Element indexElement = indexesElement.addElement(N_INDEX);
488            // add class attribute (if required)
489            if (!searchIndex.getClass().equals(CmsSearchIndex.class)) {
490                indexElement.addAttribute(A_CLASS, searchIndex.getClass().getName());
491            }
492            // add <name> element
493            indexElement.addElement(N_NAME).addText(searchIndex.getName());
494            // add <rebuild> element
495            indexElement.addElement(N_REBUILD).addText(searchIndex.getRebuildMode());
496            // add <project> element
497            indexElement.addElement(N_PROJECT).addText(searchIndex.getProject());
498            // add <locale> element
499            indexElement.addElement(N_LOCALE).addText(searchIndex.getLocale().toString());
500            // add <configuration> element
501            String fieldConfigurationName = searchIndex.getFieldConfigurationName();
502            if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(fieldConfigurationName)) {
503                indexElement.addElement(N_CONFIGURATION).addText(fieldConfigurationName);
504            }
505            // add <sources> element
506            Element sourcesElement = indexElement.addElement(N_SOURCES);
507            // iterate above sourcenames
508            Iterator<String> sourcesIterator = searchIndex.getSourceNames().iterator();
509            while (sourcesIterator.hasNext()) {
510                // add <source> element
511                sourcesElement.addElement(N_SOURCE).addText(sourcesIterator.next());
512            }
513            // iterate additional params
514            CmsParameterConfiguration indexConfiguration = searchIndex.getConfiguration();
515            if (indexConfiguration != null) {
516                indexConfiguration.appendToXml(indexElement);
517            }
518        }
519        // </indexes>
520
521        // <indexsources>
522        Element indexsourcesElement = searchElement.addElement(N_INDEXSOURCES);
523        for (CmsSearchIndexSource searchIndexSource : m_searchManager.getSearchIndexSources().values()) {
524            // add <indexsource> element(s)
525            Element indexsourceElement = indexsourcesElement.addElement(N_INDEXSOURCE);
526            // add <name> element
527            indexsourceElement.addElement(N_NAME).addText(searchIndexSource.getName());
528            // add <indexer class=""> element
529            Element indexerElement = indexsourceElement.addElement(N_INDEXER).addAttribute(
530                N_CLASS,
531                searchIndexSource.getIndexerClassName());
532            for (Entry<String, String> entry : searchIndexSource.getParams().entrySet()) {
533                // add <param name=""> element(s)
534                indexerElement.addElement(I_CmsXmlConfiguration.N_PARAM).addAttribute(
535                    I_CmsXmlConfiguration.A_NAME,
536                    entry.getKey()).addText(entry.getValue());
537            }
538            // add <resources> element
539            Element resourcesElement = indexsourceElement.addElement(N_RESOURCES);
540            Iterator<String> resourceIterator = searchIndexSource.getResourcesNames().iterator();
541            while (resourceIterator.hasNext()) {
542                // add <resource> element(s)
543                resourcesElement.addElement(N_RESOURCE).addText(resourceIterator.next());
544            }
545            // add <documenttypes-indexed> element
546            Element doctypes_indexedElement = indexsourceElement.addElement(N_DOCUMENTTYPES_INDEXED);
547            Iterator<String> doctypesIterator = searchIndexSource.getDocumentTypes().iterator();
548            while (doctypesIterator.hasNext()) {
549                // add <name> element(s)
550                doctypes_indexedElement.addElement(N_NAME).addText(doctypesIterator.next());
551            }
552        }
553        // </indexsources>
554
555        // <fieldconfigurations>
556        Element fieldConfigurationsElement = searchElement.addElement(N_FIELDCONFIGURATIONS);
557        for (I_CmsSearchFieldConfiguration fieldConfiguration : m_searchManager.getFieldConfigurations()) {
558            Element fieldConfigurationElement = fieldConfigurationsElement.addElement(N_FIELDCONFIGURATION);
559            // add class attribute (if required)
560            if (!fieldConfiguration.getClass().equals(CmsLuceneFieldConfiguration.class)) {
561                fieldConfigurationElement.addAttribute(A_CLASS, fieldConfiguration.getClass().getName());
562            }
563            fieldConfigurationElement.addElement(N_NAME).setText(fieldConfiguration.getName());
564            if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(fieldConfiguration.getDescription())) {
565                fieldConfigurationElement.addElement(N_DESCRIPTION).setText(fieldConfiguration.getDescription());
566            }
567            // search fields
568            Element fieldsElement = fieldConfigurationElement.addElement(N_FIELDS);
569            for (CmsSearchField sfield : fieldConfiguration.getFields()) {
570                if (sfield instanceof CmsLuceneField) {
571                    CmsLuceneField field = (CmsLuceneField)sfield;
572                    Element fieldElement = fieldsElement.addElement(N_FIELD);
573                    fieldElement.addAttribute(A_NAME, field.getName());
574                    if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(field.getDisplayNameForConfiguration())) {
575                        fieldElement.addAttribute(A_DISPLAY, field.getDisplayNameForConfiguration());
576                    }
577                    if (field.isCompressed()) {
578                        fieldElement.addAttribute(A_STORE, CmsLuceneField.STR_COMPRESS);
579                    } else {
580                        fieldElement.addAttribute(A_STORE, String.valueOf(field.isStored()));
581                    }
582                    String index;
583                    if (field.isIndexed()) {
584                        if (field.isTokenizedAndIndexed()) {
585                            // index and tokenized
586                            index = CmsStringUtil.TRUE;
587                        } else {
588                            // indexed but not tokenized
589                            index = CmsLuceneField.STR_UN_TOKENIZED;
590                        }
591                    } else {
592                        // not indexed at all
593                        index = CmsStringUtil.FALSE;
594                    }
595                    fieldElement.addAttribute(A_INDEX, index);
596                    if (field.isInExcerptAndStored()) {
597                        fieldElement.addAttribute(A_EXCERPT, String.valueOf(true));
598                    }
599                    if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(field.getDefaultValue())) {
600                        fieldElement.addAttribute(A_DEFAULT, field.getDefaultValue());
601                    }
602                    if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(field.getType())) {
603                        fieldElement.addAttribute(A_TYPE, field.getType());
604                    }
605                    if (field.getAnalyzer() != null) {
606                        String className = field.getAnalyzer().getClass().getName();
607                        if (className.startsWith(CmsSearchManager.LUCENE_ANALYZER)) {
608                            className = className.substring(CmsSearchManager.LUCENE_ANALYZER.length());
609                        }
610                        fieldElement.addAttribute(A_ANALYZER, className);
611                    }
612                    // field mappings
613                    for (I_CmsSearchFieldMapping mapping : field.getMappings()) {
614                        Element mappingElement = fieldElement.addElement(N_MAPPING);
615                        mappingElement.addAttribute(A_TYPE, mapping.getType().toString());
616                        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(mapping.getDefaultValue())) {
617                            mappingElement.addAttribute(A_DEFAULT, mapping.getDefaultValue());
618                        }
619                        // add class attribute (if required)
620                        if (!mapping.getClass().equals(CmsSearchFieldMapping.class)
621                            || (mapping.getType() == CmsSearchFieldMappingType.DYNAMIC)) {
622                            mappingElement.addAttribute(A_CLASS, mapping.getClass().getName());
623                        }
624                        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(mapping.getParam())) {
625                            mappingElement.setText(mapping.getParam());
626                        }
627                    }
628                }
629            }
630        }
631        // </fieldconfigurations>
632
633        return searchElement;
634    }
635
636    /**
637     * @see org.opencms.configuration.I_CmsXmlConfiguration#getDtdFilename()
638     */
639    public String getDtdFilename() {
640
641        return CONFIGURATION_DTD_NAME;
642    }
643
644    /**
645     * Returns the generated search manager.<p>
646     *
647     * @return the generated search manager
648     */
649    public CmsSearchManager getSearchManager() {
650
651        return m_searchManager;
652    }
653
654    /**
655     * Will be called when configuration of this object is finished.<p>
656     */
657    public void initializeFinished() {
658
659        if (CmsLog.INIT.isInfoEnabled()) {
660            CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_SEARCH_CONFIG_FINISHED_0));
661        }
662    }
663
664    /**
665     * Sets the generated search manager.<p>
666     *
667     * @param manager the search manager to set
668     */
669    public void setSearchManager(CmsSearchManager manager) {
670
671        m_searchManager = manager;
672        if (CmsLog.INIT.isInfoEnabled()) {
673            CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_SEARCH_MANAGER_FINISHED_0));
674        }
675    }
676
677    /**
678     * @see org.opencms.configuration.A_CmsXmlConfiguration#initMembers()
679     */
680    @Override
681    protected void initMembers() {
682
683        setXmlFileName(DEFAULT_XML_FILE_NAME);
684        if (CmsLog.INIT.isInfoEnabled()) {
685            CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_SEARCH_CONFIG_INIT_0));
686        }
687    }
688}