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            if (searchAnalyzer.getStemmerAlgorithm() != null) {
462                // add <stemmer> element
463                analyzerElement.addElement(N_STEMMER).addText(searchAnalyzer.getStemmerAlgorithm());
464            }
465            // add <locale> element
466            analyzerElement.addElement(N_LOCALE).addText(searchAnalyzer.getLocale().toString());
467        }
468        // </analyzers>
469
470        // <indexes>
471        Element indexesElement = searchElement.addElement(N_INDEXES);
472        for (CmsSearchIndex searchIndex : m_searchManager.getSearchIndexesAll()) {
473            // add the next <index> element
474            Element indexElement = indexesElement.addElement(N_INDEX);
475            // add class attribute (if required)
476            if (!searchIndex.getClass().equals(CmsSearchIndex.class)) {
477                indexElement.addAttribute(A_CLASS, searchIndex.getClass().getName());
478            }
479            // add <name> element
480            indexElement.addElement(N_NAME).addText(searchIndex.getName());
481            // add <rebuild> element
482            indexElement.addElement(N_REBUILD).addText(searchIndex.getRebuildMode());
483            // add <project> element
484            indexElement.addElement(N_PROJECT).addText(searchIndex.getProject());
485            // add <locale> element
486            indexElement.addElement(N_LOCALE).addText(searchIndex.getLocale().toString());
487            // add <configuration> element
488            String fieldConfigurationName = searchIndex.getFieldConfigurationName();
489            if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(fieldConfigurationName)) {
490                indexElement.addElement(N_CONFIGURATION).addText(fieldConfigurationName);
491            }
492            // add <sources> element
493            Element sourcesElement = indexElement.addElement(N_SOURCES);
494            // iterate above sourcenames
495            Iterator<String> sourcesIterator = searchIndex.getSourceNames().iterator();
496            while (sourcesIterator.hasNext()) {
497                // add <source> element
498                sourcesElement.addElement(N_SOURCE).addText(sourcesIterator.next());
499            }
500            // iterate additional params
501            CmsParameterConfiguration indexConfiguration = searchIndex.getConfiguration();
502            if (indexConfiguration != null) {
503                indexConfiguration.appendToXml(indexElement);
504            }
505        }
506        // </indexes>
507
508        // <indexsources>
509        Element indexsourcesElement = searchElement.addElement(N_INDEXSOURCES);
510        for (CmsSearchIndexSource searchIndexSource : m_searchManager.getSearchIndexSources().values()) {
511            // add <indexsource> element(s)
512            Element indexsourceElement = indexsourcesElement.addElement(N_INDEXSOURCE);
513            // add <name> element
514            indexsourceElement.addElement(N_NAME).addText(searchIndexSource.getName());
515            // add <indexer class=""> element
516            Element indexerElement = indexsourceElement.addElement(N_INDEXER).addAttribute(
517                N_CLASS,
518                searchIndexSource.getIndexerClassName());
519            for (Entry<String, String> entry : searchIndexSource.getParams().entrySet()) {
520                // add <param name=""> element(s)                
521                indexerElement.addElement(I_CmsXmlConfiguration.N_PARAM).addAttribute(
522                    I_CmsXmlConfiguration.A_NAME,
523                    entry.getKey()).addText(entry.getValue());
524            }
525            // add <resources> element
526            Element resourcesElement = indexsourceElement.addElement(N_RESOURCES);
527            Iterator<String> resourceIterator = searchIndexSource.getResourcesNames().iterator();
528            while (resourceIterator.hasNext()) {
529                // add <resource> element(s)
530                resourcesElement.addElement(N_RESOURCE).addText(resourceIterator.next());
531            }
532            // add <documenttypes-indexed> element
533            Element doctypes_indexedElement = indexsourceElement.addElement(N_DOCUMENTTYPES_INDEXED);
534            Iterator<String> doctypesIterator = searchIndexSource.getDocumentTypes().iterator();
535            while (doctypesIterator.hasNext()) {
536                // add <name> element(s)
537                doctypes_indexedElement.addElement(N_NAME).addText(doctypesIterator.next());
538            }
539        }
540        // </indexsources>
541
542        // <fieldconfigurations>
543        Element fieldConfigurationsElement = searchElement.addElement(N_FIELDCONFIGURATIONS);
544        for (CmsSearchFieldConfiguration fieldConfiguration : m_searchManager.getFieldConfigurations()) {
545            Element fieldConfigurationElement = fieldConfigurationsElement.addElement(N_FIELDCONFIGURATION);
546            // add class attribute (if required)
547            if (!fieldConfiguration.getClass().equals(CmsLuceneFieldConfiguration.class)) {
548                fieldConfigurationElement.addAttribute(A_CLASS, fieldConfiguration.getClass().getName());
549            }
550            fieldConfigurationElement.addElement(N_NAME).setText(fieldConfiguration.getName());
551            if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(fieldConfiguration.getDescription())) {
552                fieldConfigurationElement.addElement(N_DESCRIPTION).setText(fieldConfiguration.getDescription());
553            }
554            // search fields
555            Element fieldsElement = fieldConfigurationElement.addElement(N_FIELDS);
556            for (CmsSearchField sfield : fieldConfiguration.getFields()) {
557                if (sfield instanceof CmsLuceneField) {
558                    CmsLuceneField field = (CmsLuceneField)sfield;
559                    Element fieldElement = fieldsElement.addElement(N_FIELD);
560                    fieldElement.addAttribute(A_NAME, field.getName());
561                    if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(field.getDisplayNameForConfiguration())) {
562                        fieldElement.addAttribute(A_DISPLAY, field.getDisplayNameForConfiguration());
563                    }
564                    if (field.isCompressed()) {
565                        fieldElement.addAttribute(A_STORE, CmsLuceneField.STR_COMPRESS);
566                    } else {
567                        fieldElement.addAttribute(A_STORE, String.valueOf(field.isStored()));
568                    }
569                    String index;
570                    if (field.isIndexed()) {
571                        if (field.isTokenizedAndIndexed()) {
572                            // index and tokenized
573                            index = CmsStringUtil.TRUE;
574                        } else {
575                            // indexed but not tokenized
576                            index = CmsLuceneField.STR_UN_TOKENIZED;
577                        }
578                    } else {
579                        // not indexed at all
580                        index = CmsStringUtil.FALSE;
581                    }
582                    fieldElement.addAttribute(A_INDEX, index);
583                    if (field.getBoost() != CmsSearchField.BOOST_DEFAULT) {
584                        fieldElement.addAttribute(A_BOOST, String.valueOf(field.getBoost()));
585                    }
586                    if (field.isInExcerptAndStored()) {
587                        fieldElement.addAttribute(A_EXCERPT, String.valueOf(true));
588                    }
589                    if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(field.getDefaultValue())) {
590                        fieldElement.addAttribute(A_DEFAULT, field.getDefaultValue());
591                    }
592                    if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(field.getType())) {
593                        fieldElement.addAttribute(A_TYPE, field.getType());
594                    }
595                    if (field.getAnalyzer() != null) {
596                        String className = field.getAnalyzer().getClass().getName();
597                        if (className.startsWith(CmsSearchManager.LUCENE_ANALYZER)) {
598                            className = className.substring(CmsSearchManager.LUCENE_ANALYZER.length());
599                        }
600                        fieldElement.addAttribute(A_ANALYZER, className);
601                    }
602                    // field mappings
603                    for (I_CmsSearchFieldMapping mapping : field.getMappings()) {
604                        Element mappingElement = fieldElement.addElement(N_MAPPING);
605                        mappingElement.addAttribute(A_TYPE, mapping.getType().toString());
606                        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(mapping.getDefaultValue())) {
607                            mappingElement.addAttribute(A_DEFAULT, mapping.getDefaultValue());
608                        }
609                        // add class attribute (if required)
610                        if (!mapping.getClass().equals(CmsSearchFieldMapping.class)
611                            || (mapping.getType() == CmsSearchFieldMappingType.DYNAMIC)) {
612                            mappingElement.addAttribute(A_CLASS, mapping.getClass().getName());
613                        }
614                        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(mapping.getParam())) {
615                            mappingElement.setText(mapping.getParam());
616                        }
617                    }
618                }
619            }
620        }
621        // </fieldconfigurations>
622
623        return searchElement;
624    }
625
626    /**
627     * @see org.opencms.configuration.I_CmsXmlConfiguration#getDtdFilename()
628     */
629    public String getDtdFilename() {
630
631        return CONFIGURATION_DTD_NAME;
632    }
633
634    /**
635     * Returns the generated search manager.<p>
636     * 
637     * @return the generated search manager
638     */
639    public CmsSearchManager getSearchManager() {
640
641        return m_searchManager;
642    }
643
644    /**
645     * Will be called when configuration of this object is finished.<p> 
646     */
647    public void initializeFinished() {
648
649        if (CmsLog.INIT.isInfoEnabled()) {
650            CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_SEARCH_CONFIG_FINISHED_0));
651        }
652    }
653
654    /**
655     * Sets the generated search manager.<p>
656     * 
657     * @param manager the search manager to set
658     */
659    public void setSearchManager(CmsSearchManager manager) {
660
661        m_searchManager = manager;
662        if (CmsLog.INIT.isInfoEnabled()) {
663            CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_SEARCH_MANAGER_FINISHED_0));
664        }
665    }
666
667    /**
668     * @see org.opencms.configuration.A_CmsXmlConfiguration#initMembers()
669     */
670    @Override
671    protected void initMembers() {
672
673        setXmlFileName(DEFAULT_XML_FILE_NAME);
674        if (CmsLog.INIT.isInfoEnabled()) {
675            CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_SEARCH_CONFIG_INIT_0));
676        }
677    }
678}