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