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.file.CmsProperty;
031import org.opencms.file.collectors.I_CmsResourceCollector;
032import org.opencms.file.types.CmsResourceTypeXmlContainerPage;
033import org.opencms.file.types.CmsResourceTypeXmlContent;
034import org.opencms.file.types.I_CmsResourceType;
035import org.opencms.loader.CmsDefaultFileNameGenerator;
036import org.opencms.loader.CmsMimeType;
037import org.opencms.loader.CmsResourceManager;
038import org.opencms.loader.I_CmsResourceLoader;
039import org.opencms.main.CmsLog;
040import org.opencms.main.OpenCms;
041import org.opencms.relations.CmsRelationType;
042import org.opencms.util.CmsHtmlConverterOption;
043import org.opencms.util.CmsResourceTranslator;
044import org.opencms.util.CmsStringUtil;
045import org.opencms.widgets.I_CmsWidget;
046import org.opencms.xml.CmsXmlContentTypeManager;
047import org.opencms.xml.types.I_CmsXmlSchemaType;
048
049import java.util.ArrayList;
050import java.util.Collections;
051import java.util.Iterator;
052import java.util.List;
053
054import org.apache.commons.digester3.Digester;
055import org.apache.commons.digester3.Rule;
056
057import org.dom4j.Element;
058import org.xml.sax.Attributes;
059
060/**
061 * VFS master configuration class.<p>
062 *
063 * @since 6.0.0
064 */
065public class CmsVfsConfiguration extends A_CmsXmlConfiguration {
066
067    /** The adjust-links-folder attribute. */
068    public static final String A_ADJUST_LINKS_FOLDER = "adjust-links-folder";
069
070    /** The widget configuration attribute. */
071    public static final String A_CONFIGURATION = "configuration";
072
073    /** The widget attribute. */
074    public static final String A_DEFAULTWIDGET = "defaultwidget";
075
076    /** The extension attribute name. */
077    public static final String A_EXTENSION = "extension";
078
079    /** The source attribute name. */
080    public static final String A_SOURCE = "source";
081
082    /** The target attribute name. */
083    public static final String A_TARGET = "target";
084
085    /** The name of the DTD for this configuration. */
086    public static final String CONFIGURATION_DTD_NAME = "opencms-vfs.dtd";
087
088    /** The name of the default XML file for this configuration. */
089    public static final String DEFAULT_XML_FILE_NAME = "opencms-vfs.xml";
090
091    /** The collector node name. */
092    public static final String N_COLLECTOR = "collector";
093
094    /** The collectors node name. */
095    public static final String N_COLLECTORS = "collectors";
096
097    /** The copy-resource node name.*/
098    public static final String N_COPY_RESOURCE = "copy-resource";
099
100    /** The copy-resources node name.*/
101    public static final String N_COPY_RESOURCES = "copy-resources";
102
103    /** The defaultfile node name. */
104    public static final String N_DEFAULTFILE = "defaultfile";
105
106    /** The defaultfiles node name. */
107    public static final String N_DEFAULTFILES = "defaultfiles";
108
109    /** File translations node name. */
110    public static final String N_FILETRANSLATIONS = "filetranslations";
111
112    /** Folder translations node name. */
113    public static final String N_FOLDERTRANSLATIONS = "foldertranslations";
114
115    /** The html-converter node name.*/
116    public static final String N_HTML_CONVERTER = "html-converter";
117
118    /** The html-converters node name.*/
119    public static final String N_HTML_CONVERTERS = "html-converters";
120
121    /** The node name of an individual resource loader. */
122    public static final String N_LOADER = "loader";
123
124    /** The mapping node name. */
125    public static final String N_MAPPING = "mapping";
126
127    /** The mappings node name. */
128    public static final String N_MAPPINGS = "mappings";
129
130    /** The mimetype node name. */
131    public static final String N_MIMETYPE = "mimetype";
132
133    /** The mimetypes node name. */
134    public static final String N_MIMETYPES = "mimetypes";
135
136    /** The properties node name. */
137    public static final String N_PROPERTIES = "properties";
138
139    /** The relation type node name. */
140    public static final String N_RELATIONTYPE = "relationtype";
141
142    /** The relation types node name. */
143    public static final String N_RELATIONTYPES = "relationtypes";
144
145    /** The resource loaders node name. */
146    public static final String N_RESOURCELOADERS = "resourceloaders";
147
148    /** The main resource node name. */
149    public static final String N_RESOURCES = "resources";
150
151    /** The resource types node name. */
152    public static final String N_RESOURCETYPES = "resourcetypes";
153
154    /** The schematype node name. */
155    public static final String N_SCHEMATYPE = "schematype";
156
157    /** The schematypes node name. */
158    public static final String N_SCHEMATYPES = "schematypes";
159
160    /** Individual translation node name. */
161    public static final String N_TRANSLATION = "translation";
162
163    /** The translations master node name. */
164    public static final String N_TRANSLATIONS = "translations";
165
166    /** The node name of an individual resource type. */
167    public static final String N_TYPE = "type";
168
169    /** The node name for the version history. */
170    public static final String N_VERSIONHISTORY = "versionhistory";
171
172    /** The main vfs configuration node name. */
173    public static final String N_VFS = "vfs";
174
175    /** The widget node name. */
176    public static final String N_WIDGET = "widget";
177
178    /** The widget alias node name. */
179    public static final String N_WIDGET_ALIAS = "widget-alias";
180
181    /** The widgets node name. */
182    public static final String N_WIDGETS = "widgets";
183
184    /** The xmlcontent node name. */
185    public static final String N_XMLCONTENT = "xmlcontent";
186
187    /** The xmlcontents node name. */
188    public static final String N_XMLCONTENTS = "xmlcontents";
189
190    /** XSD translations node name. */
191    public static final String N_XSDTRANSLATIONS = "xsdtranslations";
192
193    /** The namegenerator node name. */
194    private static final String N_NAMEGENERATOR = "namegenerator";
195
196    /** The configured XML content type manager. */
197    CmsXmlContentTypeManager m_xmlContentTypeManager;
198
199    /** The list of configured default files. */
200    private List<String> m_defaultFiles;
201
202    /** Controls if file translation is enabled. */
203    private boolean m_fileTranslationEnabled;
204
205    /** The list of file translations. */
206    private List<String> m_fileTranslations;
207
208    /** Controls if folder translation is enabled. */
209    private boolean m_folderTranslationEnabled;
210
211    /** The list of folder translations. */
212    private List<String> m_folderTranslations;
213
214    /** The configured resource manager. */
215    private CmsResourceManager m_resourceManager;
216
217    /** Controls if XSD translation is enabled. */
218    private boolean m_xsdTranslationEnabled;
219
220    /** The list of XSD translations. */
221    private List<String> m_xsdTranslations;
222
223    /**
224     * Adds the resource type rules to the given digester.<p>
225     *
226     * @param digester the digester to add the rules to
227     */
228    public static void addResourceTypeXmlRules(Digester digester) {
229
230        // add rules for resource types
231        digester.addFactoryCreate("*/" + N_RESOURCETYPES + "/" + N_TYPE, CmsDigesterResourceTypeCreationFactory.class);
232        digester.addSetNext("*/" + N_RESOURCETYPES + "/" + N_TYPE, I_CmsResourceType.ADD_RESOURCE_TYPE_METHOD);
233
234        // please note: the order of the rules is very important here,
235        // the "set next" rule (above) must be added _before_ the "call method" rule (below)!
236        // reason is digester will call the rule that was last added first
237        // here we must make sure that the resource type is initialized first (with the "call method" rule)
238        // before it is actually added to the resource type container (with the "set next" rule)
239        // otherwise there will be an empty resource type added to the container, and validation will not work
240        digester.addCallMethod(
241            "*/" + N_RESOURCETYPES + "/" + N_TYPE,
242            I_CmsConfigurationParameterHandler.INIT_CONFIGURATION_METHOD,
243            3);
244        // please note: the resource types use a special version of the init method with 3 parameters
245        digester.addCallParam("*/" + N_RESOURCETYPES + "/" + N_TYPE, 0, A_NAME);
246        digester.addCallParam("*/" + N_RESOURCETYPES + "/" + N_TYPE, 1, A_ID);
247        digester.addCallParam("*/" + N_RESOURCETYPES + "/" + N_TYPE, 2, A_CLASS);
248
249        // add rules for default properties
250        digester.addObjectCreate(
251            "*/" + N_RESOURCETYPES + "/" + N_TYPE + "/" + N_PROPERTIES + "/" + N_PROPERTY,
252            CmsProperty.class);
253        digester.addCallMethod(
254            "*/" + N_RESOURCETYPES + "/" + N_TYPE + "/" + N_PROPERTIES + "/" + N_PROPERTY + "/" + N_NAME,
255            "setName",
256            1);
257        digester.addCallParam(
258            "*/" + N_RESOURCETYPES + "/" + N_TYPE + "/" + N_PROPERTIES + "/" + N_PROPERTY + "/" + N_NAME,
259            0);
260
261        digester.addCallMethod(
262            "*/" + N_RESOURCETYPES + "/" + N_TYPE + "/" + N_PROPERTIES + "/" + N_PROPERTY + "/" + N_VALUE,
263            "setValue",
264            2);
265        digester.addCallParam(
266            "*/" + N_RESOURCETYPES + "/" + N_TYPE + "/" + N_PROPERTIES + "/" + N_PROPERTY + "/" + N_VALUE,
267            0);
268        digester.addCallParam(
269            "*/" + N_RESOURCETYPES + "/" + N_TYPE + "/" + N_PROPERTIES + "/" + N_PROPERTY + "/" + N_VALUE,
270            1,
271            A_TYPE);
272
273        digester.addSetNext(
274            "*/" + N_RESOURCETYPES + "/" + N_TYPE + "/" + N_PROPERTIES + "/" + N_PROPERTY,
275            "addDefaultProperty");
276
277        // extension mapping rules
278        digester.addCallMethod(
279            "*/" + N_RESOURCETYPES + "/" + N_TYPE + "/" + N_MAPPINGS + "/" + N_MAPPING,
280            I_CmsResourceType.ADD_MAPPING_METHOD,
281            1);
282        digester.addCallParam("*/" + N_RESOURCETYPES + "/" + N_TYPE + "/" + N_MAPPINGS + "/" + N_MAPPING, 0, A_SUFFIX);
283
284        digester.addCallMethod(
285            "*/" + N_RESOURCETYPES + "/" + N_TYPE + "/" + N_COPY_RESOURCES,
286            "setAdjustLinksFolder",
287            1);
288        digester.addCallParam("*/" + N_RESOURCETYPES + "/" + N_TYPE + "/" + N_COPY_RESOURCES, 0, A_ADJUST_LINKS_FOLDER);
289
290        // copy resource rules
291        digester.addCallMethod(
292            "*/" + N_RESOURCETYPES + "/" + N_TYPE + "/" + N_COPY_RESOURCES + "/" + N_COPY_RESOURCE,
293            "addCopyResource",
294            3);
295        digester.addCallParam(
296            "*/" + N_RESOURCETYPES + "/" + N_TYPE + "/" + N_COPY_RESOURCES + "/" + N_COPY_RESOURCE,
297            0,
298            A_SOURCE);
299        digester.addCallParam(
300            "*/" + N_RESOURCETYPES + "/" + N_TYPE + "/" + N_COPY_RESOURCES + "/" + N_COPY_RESOURCE,
301            1,
302            A_TARGET);
303        digester.addCallParam(
304            "*/" + N_RESOURCETYPES + "/" + N_TYPE + "/" + N_COPY_RESOURCES + "/" + N_COPY_RESOURCE,
305            2,
306            A_TYPE);
307    }
308
309    /**
310     * Creates the xml output for resourcetype nodes.<p>
311     *
312     * @param startNode the startnode to add all rescource types to
313     * @param resourceTypes the list of resource types
314     * @param module flag, signaling to add them module resource types or not
315     */
316    public static void generateResourceTypeXml(
317        Element startNode,
318        List<I_CmsResourceType> resourceTypes,
319        boolean module) {
320
321        for (int i = 0; i < resourceTypes.size(); i++) {
322            I_CmsResourceType resType = resourceTypes.get(i);
323            // only add this resource type to the xml output, if it is no additional type defined
324            // in a module
325            if (resType.isAdditionalModuleResourceType() == module) {
326                Element resourceType = startNode.addElement(N_TYPE).addAttribute(A_CLASS, resType.getClassName());
327                // add type id and type name
328                resourceType.addAttribute(A_NAME, resType.getTypeName());
329                resourceType.addAttribute(A_ID, String.valueOf(resType.getTypeId()));
330                // add resource mappings
331                List<String> mappings = resType.getConfiguredMappings();
332                if ((mappings != null) && (mappings.size() > 0)) {
333                    Element mappingsNode = resourceType.addElement(N_MAPPINGS);
334                    for (int j = 0; j < mappings.size(); j++) {
335                        Element mapping = mappingsNode.addElement(N_MAPPING);
336                        mapping.addAttribute(A_SUFFIX, mappings.get(j));
337                    }
338                }
339                // add default properties
340                List<CmsProperty> properties = resType.getConfiguredDefaultProperties();
341                if (properties != null) {
342                    if (properties.size() > 0) {
343                        Element propertiesNode = resourceType.addElement(N_PROPERTIES);
344                        Iterator<CmsProperty> p = properties.iterator();
345                        while (p.hasNext()) {
346                            CmsProperty property = p.next();
347                            Element propertyNode = propertiesNode.addElement(N_PROPERTY);
348                            propertyNode.addElement(N_NAME).addText(property.getName());
349                            if (property.getStructureValue() != null) {
350                                propertyNode.addElement(N_VALUE).addCDATA(property.getStructureValue());
351                            }
352                            if (property.getResourceValue() != null) {
353                                propertyNode.addElement(N_VALUE).addAttribute(A_TYPE, CmsProperty.TYPE_SHARED).addCDATA(
354                                    property.getResourceValue());
355                            }
356                        }
357                    }
358                }
359                // add copy resources
360                List<CmsConfigurationCopyResource> copyRes = resType.getConfiguredCopyResources();
361                if ((copyRes != null) && (copyRes.size() > 0)) {
362                    Element copyResNode = resourceType.addElement(N_COPY_RESOURCES);
363                    Iterator<CmsConfigurationCopyResource> p = copyRes.iterator();
364                    String adjustLinksFolder = resType.getAdjustLinksFolder();
365                    if (adjustLinksFolder != null) {
366                        copyResNode.addAttribute(A_ADJUST_LINKS_FOLDER, adjustLinksFolder);
367                    }
368                    while (p.hasNext()) {
369                        CmsConfigurationCopyResource cRes = p.next();
370                        Element cNode = copyResNode.addElement(N_COPY_RESOURCE);
371                        cNode.addAttribute(A_SOURCE, cRes.getSource());
372                        if (!cRes.isTargetWasNull()) {
373                            cNode.addAttribute(A_TARGET, cRes.getTarget());
374                        }
375                        if (!cRes.isTypeWasNull()) {
376                            cNode.addAttribute(A_TYPE, cRes.getTypeString());
377                        }
378                    }
379                }
380                // add optional parameters
381                CmsParameterConfiguration configuration = resType.getConfiguration();
382                if (configuration != null) {
383                    List<String> ignore = null;
384                    if ((resType instanceof CmsResourceTypeXmlContainerPage)) {
385                        ignore = new ArrayList<String>(1);
386                        ignore.add(CmsResourceTypeXmlContent.CONFIGURATION_SCHEMA);
387                    }
388                    configuration.appendToXml(resourceType, ignore);
389                }
390            }
391        }
392    }
393
394    /**
395     * Adds a directory default file.<p>
396     *
397     * @param defaultFile the directory default file to add
398     */
399    public void addDefaultFile(String defaultFile) {
400
401        m_defaultFiles.add(defaultFile);
402        if (CmsLog.INIT.isInfoEnabled()) {
403            CmsLog.INIT.info(
404                Messages.get().getBundle().key(
405                    Messages.INIT_VFS_DEFAULT_FILE_2,
406                    new Integer(m_defaultFiles.size()),
407                    defaultFile));
408        }
409    }
410
411    /**
412     * Adds one file translation rule.<p>
413     *
414     * @param translation the file translation rule to add
415     */
416    public void addFileTranslation(String translation) {
417
418        m_fileTranslations.add(translation);
419        if (CmsLog.INIT.isInfoEnabled()) {
420            CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_VFS_ADD_FILE_TRANSLATION_1, translation));
421        }
422    }
423
424    /**
425     * Adds one folder translation rule.<p>
426     *
427     * @param translation the folder translation rule to add
428     */
429    public void addFolderTranslation(String translation) {
430
431        m_folderTranslations.add(translation);
432        if (CmsLog.INIT.isInfoEnabled()) {
433            CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_VFS_ADD_FOLDER_TRANSLATION_1, translation));
434        }
435    }
436
437    /**
438     * @see org.opencms.configuration.I_CmsXmlConfiguration#addXmlDigesterRules(org.apache.commons.digester3.Digester)
439     */
440    public void addXmlDigesterRules(Digester digester) {
441
442        // add finish rule
443        digester.addCallMethod("*/" + N_VFS, "initializeFinished");
444
445        // creation of the resource manager
446        digester.addObjectCreate("*/" + N_VFS + "/" + N_RESOURCES, CmsResourceManager.class);
447        digester.addCallMethod(
448            "*/" + N_VFS + "/" + N_RESOURCES,
449            I_CmsConfigurationParameterHandler.INIT_CONFIGURATION_METHOD);
450        digester.addSetNext("*/" + N_VFS + "/" + N_RESOURCES, "setResourceManager");
451
452        // add rules for resource loaders
453        digester.addObjectCreate(
454            "*/" + N_VFS + "/" + N_RESOURCES + "/" + N_RESOURCELOADERS + "/" + N_LOADER,
455            CmsConfigurationException.class.getName(),
456            A_CLASS);
457        digester.addCallMethod(
458            "*/" + N_VFS + "/" + N_RESOURCES + "/" + N_RESOURCELOADERS + "/" + N_LOADER,
459            I_CmsConfigurationParameterHandler.INIT_CONFIGURATION_METHOD);
460        digester.addSetNext("*/" + N_VFS + "/" + N_RESOURCES + "/" + N_RESOURCELOADERS + "/" + N_LOADER, "addLoader");
461
462        // add rules for resource types
463        addResourceTypeXmlRules(digester);
464
465        // add rules for VFS content collectors
466        digester.addCallMethod(
467            "*/" + N_VFS + "/" + N_RESOURCES + "/" + N_COLLECTORS + "/" + N_COLLECTOR,
468            "addContentCollector",
469            2);
470        digester.addCallParam("*/" + N_VFS + "/" + N_RESOURCES + "/" + N_COLLECTORS + "/" + N_COLLECTOR, 0, A_CLASS);
471        digester.addCallParam("*/" + N_VFS + "/" + N_RESOURCES + "/" + N_COLLECTORS + "/" + N_COLLECTOR, 1, A_ORDER);
472
473        // add the name generator
474        digester.addObjectCreate(
475            "*/" + N_VFS + "/" + N_RESOURCES + "/" + N_NAMEGENERATOR,
476            CmsDefaultFileNameGenerator.class.getName(),
477            A_CLASS);
478        digester.addSetNext("*/" + N_VFS + "/" + N_RESOURCES + "/" + N_NAMEGENERATOR, "setNameGenerator");
479
480        // add MIME type rules
481        digester.addCallMethod(
482            "*/" + N_VFS + "/" + N_RESOURCES + "/" + N_MIMETYPES + "/" + N_MIMETYPE,
483            "addMimeType",
484            2);
485        digester.addCallParam("*/" + N_VFS + "/" + N_RESOURCES + "/" + N_MIMETYPES + "/" + N_MIMETYPE, 0, A_EXTENSION);
486        digester.addCallParam("*/" + N_VFS + "/" + N_RESOURCES + "/" + N_MIMETYPES + "/" + N_MIMETYPE, 1, A_TYPE);
487
488        // add relation type rules
489        digester.addCallMethod(
490            "*/" + N_VFS + "/" + N_RESOURCES + "/" + N_RELATIONTYPES + "/" + N_RELATIONTYPE,
491            "addRelationType",
492            2);
493        digester.addCallParam(
494            "*/" + N_VFS + "/" + N_RESOURCES + "/" + N_RELATIONTYPES + "/" + N_RELATIONTYPE,
495            0,
496            A_NAME);
497        digester.addCallParam(
498            "*/" + N_VFS + "/" + N_RESOURCES + "/" + N_RELATIONTYPES + "/" + N_RELATIONTYPE,
499            1,
500            A_TYPE);
501
502        // add html converter rules
503        digester.addCallMethod(
504            "*/" + N_VFS + "/" + N_RESOURCES + "/" + N_HTML_CONVERTERS + "/" + N_HTML_CONVERTER,
505            "addHtmlConverter",
506            2);
507        digester.addCallParam(
508            "*/" + N_VFS + "/" + N_RESOURCES + "/" + N_HTML_CONVERTERS + "/" + N_HTML_CONVERTER,
509            0,
510            A_NAME);
511        digester.addCallParam(
512            "*/" + N_VFS + "/" + N_RESOURCES + "/" + N_HTML_CONVERTERS + "/" + N_HTML_CONVERTER,
513            1,
514            A_CLASS);
515
516        // generic <param> parameter rules
517        digester.addCallMethod(
518            "*/" + I_CmsXmlConfiguration.N_PARAM,
519            I_CmsConfigurationParameterHandler.ADD_PARAMETER_METHOD,
520            2);
521        digester.addCallParam("*/" + I_CmsXmlConfiguration.N_PARAM, 0, I_CmsXmlConfiguration.A_NAME);
522        digester.addCallParam("*/" + I_CmsXmlConfiguration.N_PARAM, 1);
523
524        // add rule for default files
525        digester.addCallMethod("*/" + N_VFS + "/" + N_DEFAULTFILES + "/" + N_DEFAULTFILE, "addDefaultFile", 1);
526        digester.addCallParam("*/" + N_VFS + "/" + N_DEFAULTFILES + "/" + N_DEFAULTFILE, 0, A_NAME);
527
528        // add rules for file translations
529        digester.addCallMethod(
530            "*/" + N_VFS + "/" + N_TRANSLATIONS + "/" + N_FILETRANSLATIONS + "/" + N_TRANSLATION,
531            "addFileTranslation",
532            0);
533        digester.addCallMethod(
534            "*/" + N_VFS + "/" + N_TRANSLATIONS + "/" + N_FILETRANSLATIONS,
535            "setFileTranslationEnabled",
536            1);
537        digester.addCallParam("*/" + N_VFS + "/" + N_TRANSLATIONS + "/" + N_FILETRANSLATIONS, 0, A_ENABLED);
538
539        // add rules for file translations
540        digester.addCallMethod(
541            "*/" + N_VFS + "/" + N_TRANSLATIONS + "/" + N_FOLDERTRANSLATIONS + "/" + N_TRANSLATION,
542            "addFolderTranslation",
543            0);
544        digester.addCallMethod(
545            "*/" + N_VFS + "/" + N_TRANSLATIONS + "/" + N_FOLDERTRANSLATIONS,
546            "setFolderTranslationEnabled",
547            1);
548        digester.addCallParam("*/" + N_VFS + "/" + N_TRANSLATIONS + "/" + N_FOLDERTRANSLATIONS, 0, A_ENABLED);
549
550        // add rules for file translations
551        digester.addCallMethod(
552            "*/" + N_VFS + "/" + N_TRANSLATIONS + "/" + N_XSDTRANSLATIONS + "/" + N_TRANSLATION,
553            "addXsdTranslation",
554            0);
555        digester.addCallMethod(
556            "*/" + N_VFS + "/" + N_TRANSLATIONS + "/" + N_XSDTRANSLATIONS,
557            "setXsdTranslationEnabled",
558            1);
559        digester.addCallParam("*/" + N_VFS + "/" + N_TRANSLATIONS + "/" + N_XSDTRANSLATIONS, 0, A_ENABLED);
560
561        // XML content type manager creation rules
562        digester.addObjectCreate("*/" + N_VFS + "/" + N_XMLCONTENT, CmsXmlContentTypeManager.class);
563        digester.addSetNext("*/" + N_VFS + "/" + N_XMLCONTENT, "setXmlContentTypeManager");
564
565        // XML content widgets add rules
566
567        // Widget definitions.
568        // 'aliases' list is used/reset by the rule for widgets, and filled by the rule for aliases.
569        final List<String> aliases = new ArrayList<>();
570        digester.addRule("*/" + N_VFS + "/" + N_XMLCONTENT + "/" + N_WIDGETS + "/" + N_WIDGET, new Rule() {
571
572            private String m_className;
573            private String m_config;
574
575            @Override
576            public void begin(String namespace, String name, Attributes attributes) throws Exception {
577
578                m_className = attributes.getValue(A_CLASS);
579                m_config = attributes.getValue(A_CONFIGURATION);
580                String alias = attributes.getValue(A_ALIAS);
581
582                aliases.clear();
583                if (alias != null) {
584                    aliases.add(alias.trim());
585                }
586            }
587
588            @Override
589            public void end(String namespace, String name) throws Exception {
590
591                CmsXmlContentTypeManager manager = getDigester().peek();
592                List<String> aliasesCopy = new ArrayList<>(aliases);
593                manager.addWidget(m_className, aliasesCopy, m_config);
594            }
595        });
596
597        digester.addRule(
598            "*/" + N_VFS + "/" + N_XMLCONTENT + "/" + N_WIDGETS + "/" + N_WIDGET + "/" + N_WIDGET_ALIAS,
599            new Rule() {
600
601                @Override
602                public void body(String namespace, String name, String text) throws Exception {
603
604                    aliases.add(text.trim());
605                }
606
607            });
608
609        // XML content schema type add rules
610        digester.addCallMethod(
611            "*/" + N_VFS + "/" + N_XMLCONTENT + "/" + N_SCHEMATYPES + "/" + N_SCHEMATYPE,
612            "addSchemaType",
613            2);
614        digester.addCallParam("*/" + N_VFS + "/" + N_XMLCONTENT + "/" + N_SCHEMATYPES + "/" + N_SCHEMATYPE, 0, A_CLASS);
615        digester.addCallParam(
616            "*/" + N_VFS + "/" + N_XMLCONTENT + "/" + N_SCHEMATYPES + "/" + N_SCHEMATYPE,
617            1,
618            A_DEFAULTWIDGET);
619    }
620
621    /**
622     * Adds one XSD translation rule.<p>
623     *
624     * @param translation the XSD translation rule to add
625     */
626    public void addXsdTranslation(String translation) {
627
628        m_xsdTranslations.add(translation);
629        if (CmsLog.INIT.isInfoEnabled()) {
630            CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_VFS_ADD_XSD_TRANSLATION_1, translation));
631        }
632    }
633
634    /**
635     * @see org.opencms.configuration.I_CmsXmlConfiguration#generateXml(org.dom4j.Element)
636     */
637    public Element generateXml(Element parent) {
638
639        if (OpenCms.getRunLevel() >= OpenCms.RUNLEVEL_3_SHELL_ACCESS) {
640            m_resourceManager = OpenCms.getResourceManager();
641            m_xmlContentTypeManager = OpenCms.getXmlContentTypeManager();
642            m_defaultFiles = OpenCms.getDefaultFiles();
643        }
644
645        // generate vfs node and subnodes
646        Element vfs = parent.addElement(N_VFS);
647
648        // add resources main element
649        Element resources = vfs.addElement(N_RESOURCES);
650
651        // add resource loader
652        Element resourceloadersElement = resources.addElement(N_RESOURCELOADERS);
653        for (I_CmsResourceLoader loader : m_resourceManager.getLoaders()) {
654            // add the loader node
655            Element loaderNode = resourceloadersElement.addElement(N_LOADER);
656            loaderNode.addAttribute(A_CLASS, loader.getClass().getName());
657            CmsParameterConfiguration loaderConfiguration = loader.getConfiguration();
658            if (loaderConfiguration != null) {
659                loaderConfiguration.appendToXml(loaderNode);
660            }
661        }
662
663        // add resource types
664        Element resourcetypesElement = resources.addElement(N_RESOURCETYPES);
665        List<I_CmsResourceType> resourceTypes = new ArrayList<I_CmsResourceType>();
666        if (m_resourceManager.getResTypeUnknownFolder() != null) {
667            resourceTypes.add(m_resourceManager.getResTypeUnknownFolder());
668        }
669        if (m_resourceManager.getResTypeUnknownFile() != null) {
670            resourceTypes.add(m_resourceManager.getResTypeUnknownFile());
671        }
672        resourceTypes.addAll(m_resourceManager.getResourceTypes());
673        generateResourceTypeXml(resourcetypesElement, resourceTypes, false);
674
675        // add VFS content collectors
676        Element collectorsElement = resources.addElement(N_COLLECTORS);
677        for (I_CmsResourceCollector collector : m_resourceManager.getRegisteredContentCollectors()) {
678            collectorsElement.addElement(N_COLLECTOR).addAttribute(
679                A_CLASS,
680                collector.getClass().getName()).addAttribute(A_ORDER, String.valueOf(collector.getOrder()));
681        }
682
683        Element namegeneratorElement = resources.addElement(N_NAMEGENERATOR);
684        String nameGeneratorClass = m_resourceManager.getNameGenerator().getClass().getName();
685        namegeneratorElement.addAttribute(A_CLASS, nameGeneratorClass);
686
687        // add MIME types
688        Element mimeTypesElement = resources.addElement(N_MIMETYPES);
689        for (CmsMimeType type : m_resourceManager.getMimeTypes()) {
690            mimeTypesElement.addElement(N_MIMETYPE).addAttribute(A_EXTENSION, type.getExtension()).addAttribute(
691                A_TYPE,
692                type.getType());
693        }
694
695        // add relation types
696        Element relationTypesElement = resources.addElement(N_RELATIONTYPES);
697        for (CmsRelationType type : m_resourceManager.getRelationTypes()) {
698            relationTypesElement.addElement(N_RELATIONTYPE).addAttribute(A_NAME, type.getName()).addAttribute(
699                A_TYPE,
700                type.getType());
701        }
702
703        // HTML converter configuration
704        boolean writeConfig = false;
705        for (CmsHtmlConverterOption converter : m_resourceManager.getHtmlConverters()) {
706            if (!converter.isDefault()) {
707                // found a non default converter configuration, set flag to write configuration
708                writeConfig = true;
709                break;
710            }
711        }
712        if (writeConfig) {
713            // configuration is written because non default options were found
714            Element htmlConvertersElement = resources.addElement(N_HTML_CONVERTERS);
715            for (CmsHtmlConverterOption converter : m_resourceManager.getHtmlConverters()) {
716                Element converterElement = htmlConvertersElement.addElement(N_HTML_CONVERTER).addAttribute(
717                    A_NAME,
718                    converter.getName());
719                converterElement.addAttribute(A_CLASS, converter.getClassName());
720            }
721        }
722
723        // add default file names
724        Element defaultFileElement = vfs.addElement(N_DEFAULTFILES);
725        for (String element : m_defaultFiles) {
726            defaultFileElement.addElement(N_DEFAULTFILE).addAttribute(A_NAME, element);
727        }
728
729        // add translation rules
730        Element translationsElement = vfs.addElement(N_TRANSLATIONS);
731
732        // file translation rules
733        Element fileTransElement = translationsElement.addElement(N_FILETRANSLATIONS).addAttribute(
734            A_ENABLED,
735            String.valueOf(m_fileTranslationEnabled));
736        for (String translation : m_fileTranslations) {
737            fileTransElement.addElement(N_TRANSLATION).setText(translation);
738        }
739
740        // folder translation rules
741        Element folderTransElement = translationsElement.addElement(N_FOLDERTRANSLATIONS).addAttribute(
742            A_ENABLED,
743            String.valueOf(m_folderTranslationEnabled));
744        for (String translation : m_folderTranslations) {
745            folderTransElement.addElement(N_TRANSLATION).setText(translation);
746        }
747
748        // XSD translation rules
749        Element xsdTransElement = translationsElement.addElement(N_XSDTRANSLATIONS).addAttribute(
750            A_ENABLED,
751            String.valueOf(m_xsdTranslationEnabled));
752        for (String translation : m_xsdTranslations) {
753            xsdTransElement.addElement(N_TRANSLATION).setText(translation);
754        }
755
756        // XML content configuration
757        Element xmlContentsElement = vfs.addElement(N_XMLCONTENT);
758
759        // XML widgets
760        Element xmlWidgetsElement = xmlContentsElement.addElement(N_WIDGETS);
761        for (String widget : m_xmlContentTypeManager.getRegisteredWidgetNames()) {
762            Element widgetElement = xmlWidgetsElement.addElement(N_WIDGET).addAttribute(A_CLASS, widget);
763            for (String alias : m_xmlContentTypeManager.getRegisteredWidgetAliases(widget)) {
764                widgetElement.addElement(N_WIDGET_ALIAS).addText(alias);
765            }
766            String defaultConfiguration = m_xmlContentTypeManager.getWidgetDefaultConfiguration(widget);
767            if (CmsStringUtil.isNotEmpty(defaultConfiguration)) {
768                widgetElement.addAttribute(A_CONFIGURATION, defaultConfiguration);
769            }
770        }
771
772        // XML content types
773        Element xmlSchemaTypesElement = xmlContentsElement.addElement(N_SCHEMATYPES);
774        for (I_CmsXmlSchemaType type : m_xmlContentTypeManager.getRegisteredSchemaTypes()) {
775            I_CmsWidget widget = m_xmlContentTypeManager.getWidgetDefault(type.getTypeName());
776            xmlSchemaTypesElement.addElement(N_SCHEMATYPE).addAttribute(
777                A_CLASS,
778                type.getClass().getName()).addAttribute(A_DEFAULTWIDGET, widget.getClass().getName());
779        }
780
781        // return the vfs node
782        return vfs;
783    }
784
785    /**
786     * Returns the (unmodifiable) list of configured directory default files.<p>
787     *
788     * @return the (unmodifiable) list of configured directory default files
789     */
790    public List<String> getDefaultFiles() {
791
792        return Collections.unmodifiableList(m_defaultFiles);
793    }
794
795    /**
796     * @see org.opencms.configuration.I_CmsXmlConfiguration#getDtdFilename()
797     */
798    public String getDtdFilename() {
799
800        return CONFIGURATION_DTD_NAME;
801    }
802
803    /**
804     * Returns the file resource translator that has been initialized
805     * with the configured file translation rules.<p>
806     *
807     * @return the file resource translator
808     */
809    public CmsResourceTranslator getFileTranslator() {
810
811        String[] array = new String[0];
812        if (m_fileTranslationEnabled) {
813            array = new String[m_fileTranslations.size()];
814            for (int i = 0; i < m_fileTranslations.size(); i++) {
815                array[i] = m_fileTranslations.get(i);
816            }
817        }
818        return new CmsResourceTranslator(array, true);
819    }
820
821    /**
822     * Returns the folder resource translator that has been initialized
823     * with the configured folder translation rules.<p>
824     *
825     * @return the folder resource translator
826     */
827    public CmsResourceTranslator getFolderTranslator() {
828
829        String[] array = new String[0];
830        if (m_folderTranslationEnabled) {
831            array = new String[m_folderTranslations.size()];
832            for (int i = 0; i < m_folderTranslations.size(); i++) {
833                array[i] = m_folderTranslations.get(i);
834            }
835        }
836        return new CmsResourceTranslator(array, false);
837    }
838
839    /**
840     * Returns the initialized resource manager.<p>
841     *
842     * @return the initialized resource manager
843     */
844    public CmsResourceManager getResourceManager() {
845
846        return m_resourceManager;
847    }
848
849    /**
850     * Returns the configured XML content type manager.<p>
851     *
852     * @return the configured XML content type manager
853     */
854    public CmsXmlContentTypeManager getXmlContentTypeManager() {
855
856        return m_xmlContentTypeManager;
857    }
858
859    /**
860     * Returns the XSD translator that has been initialized
861     * with the configured XSD translation rules.<p>
862     *
863     * @return the XSD translator
864     */
865    public CmsResourceTranslator getXsdTranslator() {
866
867        String[] array = m_xsdTranslationEnabled ? new String[m_xsdTranslations.size()] : new String[0];
868        for (int i = 0; i < m_xsdTranslations.size(); i++) {
869            array[i] = m_xsdTranslations.get(i);
870        }
871        return new CmsResourceTranslator(array, true);
872    }
873
874    /**
875     * Will be called when configuration of this object is finished.<p>
876     */
877    public void initializeFinished() {
878
879        if (CmsLog.INIT.isInfoEnabled()) {
880            CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_VFS_CONFIG_FINISHED_0));
881        }
882    }
883
884    /**
885     * Enables or disables the file translation rules.<p>
886     *
887     * @param value if <code>"true"</code>, file translation is enabled, otherwise it is disabled
888     */
889    public void setFileTranslationEnabled(String value) {
890
891        m_fileTranslationEnabled = Boolean.valueOf(value).booleanValue();
892        if (CmsLog.INIT.isInfoEnabled()) {
893            if (m_fileTranslationEnabled) {
894                CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_VFS_FILE_TRANSLATION_ENABLE_0));
895            } else {
896                CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_VFS_FILE_TRANSLATION_DISABLE_0));
897            }
898        }
899    }
900
901    /**
902     * Enables or disables the folder translation rules.<p>
903     *
904     * @param value if <code>"true"</code>, folder translation is enabled, otherwise it is disabled
905     */
906    public void setFolderTranslationEnabled(String value) {
907
908        m_folderTranslationEnabled = Boolean.valueOf(value).booleanValue();
909        if (CmsLog.INIT.isInfoEnabled()) {
910            if (m_folderTranslationEnabled) {
911                CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_VFS_FOLDER_TRANSLATION_ENABLE_0));
912            } else {
913                CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_VFS_FOLDER_TRANSLATION_DISABLE_0));
914            }
915        }
916    }
917
918    /**
919     * Sets the generated resource manager.<p>
920     *
921     * @param manager the resource manager to set
922     */
923    public void setResourceManager(CmsResourceManager manager) {
924
925        m_resourceManager = manager;
926    }
927
928    /**
929     * Sets the generated XML content type manager.<p>
930     *
931     * @param manager the generated XML content type manager to set
932     */
933    public void setXmlContentTypeManager(CmsXmlContentTypeManager manager) {
934
935        if (CmsLog.INIT.isInfoEnabled()) {
936            CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_VFS_XML_CONTENT_FINISHED_0));
937        }
938        m_xmlContentTypeManager = manager;
939    }
940
941    /**
942     * Enables or disables the XSD translation rules.<p>
943     *
944     * @param value if <code>"true"</code>, XSD translation is enabled, otherwise it is disabled
945     */
946    public void setXsdTranslationEnabled(String value) {
947
948        m_xsdTranslationEnabled = Boolean.valueOf(value).booleanValue();
949        if (CmsLog.INIT.isInfoEnabled()) {
950            if (m_xsdTranslationEnabled) {
951                CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_VFS_XSD_TRANSLATION_ENABLE_0));
952            } else {
953                CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_VFS_XSD_TRANSLATION_DISABLE_0));
954            }
955        }
956    }
957
958    /**
959     * @see org.opencms.configuration.A_CmsXmlConfiguration#initMembers()
960     */
961    @Override
962    protected void initMembers() {
963
964        setXmlFileName(DEFAULT_XML_FILE_NAME);
965        m_fileTranslations = new ArrayList<String>();
966        m_folderTranslations = new ArrayList<String>();
967        m_xsdTranslations = new ArrayList<String>();
968        m_defaultFiles = new ArrayList<String>();
969        if (CmsLog.INIT.isInfoEnabled()) {
970            CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_VFS_CONFIG_INIT_0));
971        }
972    }
973}