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