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.module;
029
030import org.opencms.db.CmsExportPoint;
031import org.opencms.file.CmsObject;
032import org.opencms.file.CmsResource;
033import org.opencms.file.CmsResourceFilter;
034import org.opencms.file.types.I_CmsResourceType;
035import org.opencms.main.CmsException;
036import org.opencms.main.CmsIllegalArgumentException;
037import org.opencms.main.CmsLog;
038import org.opencms.main.OpenCms;
039import org.opencms.security.CmsRole;
040import org.opencms.security.CmsRoleViolationException;
041import org.opencms.util.CmsFileUtil;
042import org.opencms.util.CmsStringUtil;
043import org.opencms.workplace.explorer.CmsExplorerTypeSettings;
044
045import java.util.ArrayList;
046import java.util.Collections;
047import java.util.List;
048import java.util.Map;
049import java.util.SortedMap;
050import java.util.StringTokenizer;
051import java.util.TreeMap;
052
053import org.apache.commons.logging.Log;
054
055/**
056 * Describes an OpenCms module.<p>
057 *
058 * OpenCms modules provide a standard mechanism to extend the OpenCms functionality.
059 * Modules can contain VFS data, Java classes and a number of configuration options.<p>
060 *
061 * @since 6.0.0
062 *
063 * @see org.opencms.module.I_CmsModuleAction
064 * @see org.opencms.module.A_CmsModuleAction
065 */
066public class CmsModule implements Comparable<CmsModule> {
067
068    /** The available module export modes. */
069    public enum ExportMode {
070        /** Default export mode. */
071        DEFAULT, /** Reduced export, that omits last modification information (dates and users). */
072        REDUCED;
073
074        /**
075         * @see java.lang.Enum#toString()
076         */
077        @Override
078        public String toString() {
079
080            return super.toString().toLowerCase();
081        }
082    }
083
084    /** The default date for module created / installed if not provided. */
085    public static final long DEFAULT_DATE = 0L;
086
087    /** The log object for this class. */
088    private static final Log LOG = CmsLog.getLog(CmsModule.class);
089
090    /**
091     * The module property key name to specifiy additional resources which are
092     * part of a module outside of {system/modules}.
093     */
094    private static final String MODULE_PROPERTY_ADDITIONAL_RESOURCES = "additionalresources";
095
096    /** Character to separate additional resources specified in the module properties.  */
097    private static final String MODULE_PROPERTY_ADDITIONAL_RESOURCES_SEPARATOR = ";";
098
099    /** The module action class name. */
100    private String m_actionClass;
101
102    /** Initialized module action instance. */
103    private I_CmsModuleAction m_actionInstance;
104
105    /** The email of the author of this module. */
106    private String m_authorEmail;
107
108    /** The name of the author of this module. */
109    private String m_authorName;
110
111    /** Flag to create the classes folders when creating the module. */
112    private boolean m_createClassesFolder;
113
114    /** Flag to create the elements folder when creating the module. */
115    private boolean m_createElementsFolder;
116
117    /** Flag to create the formatters folder when creating the module. */
118    private boolean m_createFormattersFolder;
119
120    /** Flag to create the lib folder when creating the module. */
121    private boolean m_createLibFolder;
122
123    /** Flag to create the module folder when creating the module. */
124    private boolean m_createModuleFolder;
125
126    /** Flag to create the resources folder when creating the module. */
127    private boolean m_createResourcesFolder;
128
129    /** Flag to create the schemas folder when creating the module. */
130    private boolean m_createSchemasFolder;
131
132    /** Flag to create the template folder when creating the module. */
133    private boolean m_createTemplateFolder;
134
135    /** The date this module was created by the author. */
136    private long m_dateCreated;
137
138    /** The date this module was installed. */
139    private long m_dateInstalled;
140
141    /** List of dependencies of this module. */
142    private List<CmsModuleDependency> m_dependencies;
143
144    /** The description of this module. */
145    private String m_description;
146
147    /** The explorer type settings. */
148    private List<CmsExplorerTypeSettings> m_explorerTypeSettings;
149
150    /** List of export points added by this module. */
151    private List<CmsExportPoint> m_exportPoints;
152
153    /** Indicates if this modules configuration has already been frozen. */
154    private boolean m_frozen;
155
156    /** The group of the module. */
157    private String m_group;
158
159    /** The script to execute when the module is imported. */
160    private String m_importScript;
161
162    /** The import site. */
163    private String m_importSite;
164
165    /** The name of this module, must be a valid Java package name. */
166    private String m_name;
167
168    /** The "nice" display name of this module. */
169    private String m_niceName;
170
171    /** A timestamp from the time this object was created. */
172    private long m_objectCreateTime = System.currentTimeMillis();
173
174    /** The additional configuration parameters of this module. */
175    private SortedMap<String, String> m_parameters;
176
177    /** The export mode to use for the module. */
178    private ExportMode m_exportMode;
179
180    /** List of VFS resources that belong to this module. */
181    private List<String> m_resources;
182
183    /** List of VFS resources that do not belong to this module.
184     *  In particular used for files / folders in folders that belong to the module.
185     */
186    private List<String> m_excluderesources;
187
188    /** The list of additional resource types. */
189    private List<I_CmsResourceType> m_resourceTypes;
190
191    /** The name of the user who installed this module. */
192    private String m_userInstalled;
193
194    /** The version of this module. */
195    private CmsModuleVersion m_version;
196
197    /**
198     * Creates a new, empty CmsModule object.<p>
199     */
200    public CmsModule() {
201
202        m_version = new CmsModuleVersion(CmsModuleVersion.DEFAULT_VERSION);
203        m_resources = Collections.emptyList();
204        m_excluderesources = Collections.emptyList();
205        m_exportPoints = Collections.emptyList();
206        m_dependencies = Collections.emptyList();
207        m_exportMode = ExportMode.DEFAULT;
208    }
209
210    /**
211     * Creates a new module description with the specified values.<p>
212     *
213     * @param name the name of this module, must be a valid Java package name
214     * @param niceName the "nice" display name of this module
215     * @param group the group of this module
216     * @param actionClass the (optional) module class name
217     * @param importScript the script to execute when the module is imported
218     * @param importSite the site root into which this module should be imported
219     * @param exportMode the export mode that should be used for the module
220     * @param description the description of this module
221     * @param version the version of this module
222     * @param authorName the name of the author of this module
223     * @param authorEmail the email of the author of this module
224     * @param dateCreated the date this module was created by the author
225     * @param userInstalled the name of the user who uploaded this module
226     * @param dateInstalled the date this module was uploaded
227     * @param dependencies a list of dependencies of this module
228     * @param exportPoints a list of export point added by this module
229     * @param resources a list of VFS resources that belong to this module
230     * @param excluderesources a list of VFS resources that are exclude from the module's resources
231     * @param parameters the parameters for this module
232     */
233    public CmsModule(
234        String name,
235        String niceName,
236        String group,
237        String actionClass,
238        String importScript,
239        String importSite,
240        ExportMode exportMode,
241        String description,
242        CmsModuleVersion version,
243        String authorName,
244        String authorEmail,
245        long dateCreated,
246        String userInstalled,
247        long dateInstalled,
248        List<CmsModuleDependency> dependencies,
249        List<CmsExportPoint> exportPoints,
250        List<String> resources,
251        List<String> excluderesources,
252        Map<String, String> parameters) {
253
254        super();
255        m_name = name;
256        setNiceName(niceName);
257        setActionClass(actionClass);
258        setGroup(group);
259
260        m_exportMode = null == exportMode ? ExportMode.DEFAULT : exportMode;
261
262        if (CmsStringUtil.isEmpty(description)) {
263            m_description = "";
264        } else {
265            m_description = description;
266        }
267        m_version = version;
268        if (CmsStringUtil.isEmpty(authorName)) {
269            m_authorName = "";
270        } else {
271            m_authorName = authorName;
272        }
273        if (CmsStringUtil.isEmpty(authorEmail)) {
274            m_authorEmail = "";
275        } else {
276            m_authorEmail = authorEmail;
277        }
278        // remove milisecounds
279        m_dateCreated = (dateCreated / 1000L) * 1000L;
280        if (CmsStringUtil.isEmpty(userInstalled)) {
281            m_userInstalled = "";
282        } else {
283            m_userInstalled = userInstalled;
284        }
285        m_dateInstalled = (dateInstalled / 1000L) * 1000L;
286        if (dependencies == null) {
287            m_dependencies = Collections.emptyList();
288        } else {
289            m_dependencies = Collections.unmodifiableList(dependencies);
290        }
291        if (exportPoints == null) {
292            m_exportPoints = Collections.emptyList();
293        } else {
294            m_exportPoints = Collections.unmodifiableList(exportPoints);
295        }
296        if (resources == null) {
297            m_resources = Collections.emptyList();
298        } else {
299            m_resources = Collections.unmodifiableList(resources);
300        }
301        if (excluderesources == null) {
302            m_excluderesources = Collections.emptyList();
303        } else {
304            m_excluderesources = Collections.unmodifiableList(excluderesources);
305        }
306        if (parameters == null) {
307            m_parameters = new TreeMap<String, String>();
308        } else {
309            m_parameters = new TreeMap<String, String>(parameters);
310        }
311        m_importSite = importSite;
312
313        m_importScript = importScript;
314
315        initOldAdditionalResources();
316
317        if (LOG.isDebugEnabled()) {
318            LOG.debug(Messages.get().getBundle().key(Messages.LOG_MODULE_INSTANCE_CREATED_1, m_name));
319        }
320        m_resourceTypes = Collections.emptyList();
321        m_explorerTypeSettings = Collections.emptyList();
322    }
323
324    /** Determines the resources that are:
325     * <ul>
326     *  <li>accessible with the provided {@link CmsObject},</li>
327     *  <li>part of the <code>moduleResources</code> (or in a folder of these resources) and</li>
328     *  <li><em>not</em> contained in <code>excludedResources</code> (or a folder of these resources).</li>
329     * </ul>
330     * and adds the to <code>result</code>
331     *
332     * @param result the resource list, that gets extended by the calculated resources.
333     * @param cms the {@link CmsObject} used to read resources.
334     * @param moduleResources the resources to include.
335     * @param excludeResources the site paths of the resources to exclude.
336     * @throws CmsException thrown if reading resources fails.
337     */
338    public static void addCalculatedModuleResources(
339        List<CmsResource> result,
340        final CmsObject cms,
341        final List<CmsResource> moduleResources,
342        final List<String> excludeResources) throws CmsException {
343
344        for (CmsResource resource : moduleResources) {
345
346            String sitePath = cms.getSitePath(resource);
347
348            List<String> excludedSubResources = getExcludedForResource(sitePath, excludeResources);
349
350            // check if resources have to be excluded
351            if (excludedSubResources.isEmpty()) {
352                // no resource has to be excluded - add the whole resource
353                // (that is, also all resources in the folder, if the resource is a folder)
354                result.add(resource);
355            } else {
356                // cannot add the complete resource (i.e., including the whole sub-tree)
357                if (sitePath.equals(excludedSubResources.get(0))) {
358                    // the resource itself is excluded -> do not add it and check the next resource
359                    continue;
360                }
361                // try to include sub-resources.
362                List<CmsResource> subResources = cms.readResources(sitePath, CmsResourceFilter.ALL, false);
363                addCalculatedModuleResources(result, cms, subResources, excludedSubResources);
364            }
365        }
366
367    }
368
369    /** Calculates the resources belonging to the module, taking excluded resources and readability of resources into account,
370     *  and returns site paths of the module resources.<p>
371     *  For more details of the returned resource, see {@link #calculateModuleResources(CmsObject, CmsModule)}.
372     *
373     * @param cms the {@link CmsObject} used to read the resources.
374     * @param module the module, for which the resources should be calculated
375     * @return the calculated module resources
376     * @throws CmsException thrown if reading resources fails.
377     */
378    public static List<String> calculateModuleResourceNames(final CmsObject cms, final CmsModule module)
379    throws CmsException {
380
381        // adjust the site root, if necessary
382        CmsObject cmsClone = adjustSiteRootIfNecessary(cms, module);
383
384        // calculate the module resources
385        List<CmsResource> moduleResources = calculateModuleResources(cmsClone, module);
386
387        // get the site paths
388        List<String> moduleResouceNames = new ArrayList<String>(moduleResources.size());
389        for (CmsResource resource : moduleResources) {
390            moduleResouceNames.add(cmsClone.getSitePath(resource));
391        }
392        return moduleResouceNames;
393    }
394
395    /** Calculates and returns the resources belonging to the module, taking excluded resources and readability of resources into account.
396     * The list of returned resources contains:
397     * <ul>
398     *  <li>Only resources that are readable (present at the system and accessible with the provided {@link CmsObject}</li>
399     *  <li>Only the resource for a folder, if <em>all</em> resources in the folder belong to the module.</li>
400     *  <li>Only resources that are specified as module resources and <em>not</em> excluded by the module's exclude resources.</li>
401     * </ul>
402     *
403     * @param cms the {@link CmsObject} used to read the resources.
404     * @param module the module, for which the resources should be calculated
405     * @return the calculated module resources
406     * @throws CmsException thrown if reading resources fails.
407     */
408    public static List<CmsResource> calculateModuleResources(final CmsObject cms, final CmsModule module)
409    throws CmsException {
410
411        CmsObject cmsClone = adjustSiteRootIfNecessary(cms, module);
412        List<CmsResource> result = null;
413        List<String> excluded = CmsFileUtil.removeRedundancies(module.getExcludeResources());
414        excluded = removeNonAccessible(cmsClone, excluded);
415        List<String> resourceSitePaths = CmsFileUtil.removeRedundancies(module.getResources());
416        resourceSitePaths = removeNonAccessible(cmsClone, resourceSitePaths);
417
418        List<CmsResource> moduleResources = new ArrayList<CmsResource>(resourceSitePaths.size());
419        for (String resourceSitePath : resourceSitePaths) {
420            // assumes resources are accessible - already checked aboveremoveNonAccessible
421            CmsResource resource = cmsClone.readResource(resourceSitePath);
422            moduleResources.add(resource);
423        }
424
425        if (excluded.isEmpty()) {
426            result = moduleResources;
427        } else {
428            result = new ArrayList<CmsResource>();
429
430            addCalculatedModuleResources(result, cmsClone, moduleResources, excluded);
431
432        }
433        return result;
434
435    }
436
437    /** Adjusts the site root and returns a cloned CmsObject, iff the module has set an import site that differs
438     * from the site root of the CmsObject provided as argument. Otherwise returns the provided CmsObject unchanged.
439     * @param cms The original CmsObject.
440     * @param module The module where the import site is read from.
441     * @return The original CmsObject, or, if necessary, a clone with adjusted site root
442     * @throws CmsException see {@link OpenCms#initCmsObject(CmsObject)}
443     */
444    private static CmsObject adjustSiteRootIfNecessary(final CmsObject cms, final CmsModule module)
445    throws CmsException {
446
447        CmsObject cmsClone;
448        if ((null == module.getImportSite()) || cms.getRequestContext().getSiteRoot().equals(module.getImportSite())) {
449            cmsClone = cms;
450        } else {
451            cmsClone = OpenCms.initCmsObject(cms);
452            cmsClone.getRequestContext().setSiteRoot(module.getImportSite());
453        }
454
455        return cmsClone;
456    }
457
458    /** Returns only the resource names starting with the provided <code>sitePath</code>.
459     *
460     * @param sitePath the site relative path, all paths should start with.
461     * @param excluded the paths to filter.
462     * @return the paths from <code>excluded</code>, that start with <code>sitePath</code>.
463     */
464    private static List<String> getExcludedForResource(final String sitePath, final List<String> excluded) {
465
466        List<String> result = new ArrayList<String>();
467        for (String exclude : excluded) {
468            if (exclude.startsWith(sitePath)) {
469                result.add(exclude);
470            }
471        }
472        return result;
473    }
474
475    /** Removes the resources not accessible with the provided {@link CmsObject}.
476     *
477     * @param cms the {@link CmsObject} used to read the resources.
478     * @param sitePaths site relative paths of the resources that should be checked for accessibility.
479     * @return site paths of the accessible resources.
480     */
481    private static List<String> removeNonAccessible(CmsObject cms, List<String> sitePaths) {
482
483        List<String> result = new ArrayList<String>(sitePaths.size());
484        for (String sitePath : sitePaths) {
485            if (cms.existsResource(sitePath, CmsResourceFilter.ALL)) {
486                result.add(sitePath);
487            }
488        }
489        return result;
490    }
491
492    /**
493     * Checks if this module depends on another given module,
494     * will return the dependency, or <code>null</code> if no dependency was found.<p>
495     *
496     * @param module the other module to check against
497     * @return the dependency, or null if no dependency was found
498     */
499    public CmsModuleDependency checkDependency(CmsModule module) {
500
501        CmsModuleDependency otherDepdendency = new CmsModuleDependency(module.getName(), module.getVersion());
502
503        // loop through all the dependencies
504        for (int i = 0; i < m_dependencies.size(); i++) {
505            CmsModuleDependency dependency = m_dependencies.get(i);
506            if (dependency.dependesOn(otherDepdendency)) {
507                // short circuit here
508                return dependency;
509            }
510        }
511
512        // no dependency was found
513        return null;
514    }
515
516    /**
517     * Checks if all resources of the module are present.<p>
518     *
519     * @param cms an initialized OpenCms user context which must have read access to all module resources
520     *
521     * @throws CmsIllegalArgumentException in case not all module resources exist or can be read with the given OpenCms user context
522     */
523    public void checkResources(CmsObject cms) throws CmsIllegalArgumentException {
524
525        CmsFileUtil.checkResources(cms, getResources());
526    }
527
528    /**
529     * Clones a CmsModule which is not set to frozen.<p>
530     * This clones module can be used to be update the module information.
531     *
532     * @see java.lang.Object#clone()
533     */
534    @Override
535    public Object clone() {
536
537        // create a copy of the module
538        CmsModule result = new CmsModule(
539            m_name,
540            m_niceName,
541            m_group,
542            m_actionClass,
543            m_importScript,
544            m_importSite,
545            m_exportMode,
546            m_description,
547            m_version,
548            m_authorName,
549            m_authorEmail,
550            m_dateCreated,
551            m_userInstalled,
552            m_dateInstalled,
553            m_dependencies,
554            m_exportPoints,
555            m_resources,
556            m_excluderesources,
557            m_parameters);
558        // and set its frozen state to false
559        result.m_frozen = false;
560
561        if (getExplorerTypes() != null) {
562            List<CmsExplorerTypeSettings> settings = new ArrayList<CmsExplorerTypeSettings>();
563            for (CmsExplorerTypeSettings setting : getExplorerTypes()) {
564                settings.add((CmsExplorerTypeSettings)setting.clone());
565            }
566            result.setExplorerTypes(settings);
567        }
568        if (getResourceTypes() != null) {
569            // TODO: The resource types must be cloned also, otherwise modification will effect the origin also
570            result.setResourceTypes(new ArrayList<I_CmsResourceType>(getResourceTypes()));
571        }
572        if (getDependencies() != null) {
573            List<CmsModuleDependency> deps = new ArrayList<CmsModuleDependency>();
574            for (CmsModuleDependency dep : getDependencies()) {
575                deps.add((CmsModuleDependency)dep.clone());
576            }
577            result.setDependencies(new ArrayList<CmsModuleDependency>(getDependencies()));
578        }
579        if (getExportPoints() != null) {
580            List<CmsExportPoint> exps = new ArrayList<CmsExportPoint>();
581            for (CmsExportPoint exp : getExportPoints()) {
582                exps.add((CmsExportPoint)exp.clone());
583            }
584            result.setExportPoints(exps);
585        }
586
587        result.setCreateClassesFolder(m_createClassesFolder);
588        result.setCreateElementsFolder(m_createElementsFolder);
589        result.setCreateLibFolder(m_createLibFolder);
590        result.setCreateModuleFolder(m_createModuleFolder);
591        result.setCreateResourcesFolder(m_createResourcesFolder);
592        result.setCreateSchemasFolder(m_createSchemasFolder);
593        result.setCreateTemplateFolder(m_createTemplateFolder);
594        result.setCreateFormattersFolder(m_createFormattersFolder);
595
596        result.setResources(new ArrayList<String>(m_resources));
597        result.setExcludeResources(new ArrayList<String>(m_excluderesources));
598
599        return result;
600    }
601
602    /**
603     * @see java.lang.Comparable#compareTo(java.lang.Object)
604     */
605    public int compareTo(CmsModule obj) {
606
607        if (obj == this) {
608            return 0;
609        }
610        return m_name.compareTo(obj.m_name);
611    }
612
613    /**
614     * Two instances of a module are considered equal if their name is equal.<p>
615     *
616     * @param obj the object to compare
617     *
618     * @return true if the objects are equal
619     *
620     * @see java.lang.Object#equals(java.lang.Object)
621     * @see #isIdentical(CmsModule)
622     */
623    @Override
624    public boolean equals(Object obj) {
625
626        if (obj == this) {
627            return true;
628        }
629        if (obj instanceof CmsModule) {
630            return ((CmsModule)obj).m_name.equals(m_name);
631        }
632        return false;
633    }
634
635    /**
636     * Returns the class name of this modules (optional) action class.<p>
637     *
638     * If this module does not use an action class,
639     * <code>null</code> is returned.<p>
640     *
641     * @return the class name of this modules (optional) action class
642     */
643    public String getActionClass() {
644
645        return m_actionClass;
646    }
647
648    /**
649     * Returns the module action instance of this module, or <code>null</code>
650     * if no module action instance is configured.<p>
651     *
652     * @return the module action instance of this module
653     */
654    public I_CmsModuleAction getActionInstance() {
655
656        return m_actionInstance;
657    }
658
659    /**
660     * Returns the email of the module author.<p>
661     *
662     * @return the email of the module author
663     */
664    public String getAuthorEmail() {
665
666        return m_authorEmail;
667    }
668
669    /**
670     * Returns the name of the author of this module.<p>
671     *
672     * @return the name of the author of this module
673     */
674    public String getAuthorName() {
675
676        return m_authorName;
677    }
678
679    /**
680     * Gets the module configuration path.<p>
681     *
682     * @return the module configuration path
683     */
684    public String getConfigurationPath() {
685
686        String parameter = getParameter("config.sitemap");
687        if (parameter != null) {
688            return parameter;
689        } else {
690            return "/system/modules/" + getName() + "/.config";
691        }
692    }
693
694    /**
695     * Returns the date this module was created by the author.<p>
696     *
697     * @return the date this module was created by the author
698     */
699    public long getDateCreated() {
700
701        return m_dateCreated;
702    }
703
704    /**
705     * Returns the date this module was uploaded.<p>
706     *
707     * @return the date this module was uploaded
708     */
709    public long getDateInstalled() {
710
711        return m_dateInstalled;
712    }
713
714    /**
715     * Returns the list of dependencies of this module.<p>
716     *
717     * @return the list of dependencies of this module
718     */
719    public List<CmsModuleDependency> getDependencies() {
720
721        return m_dependencies;
722    }
723
724    /**
725     * Returns the description of this module.<p>
726     *
727     * @return the description of this module
728     */
729    public String getDescription() {
730
731        return m_description;
732    }
733
734    /**
735     * Returns the list of VFS resources that do not belong to this module.<p>
736     * In particular, files / folders that would be included otherwise,
737     * considering the module resources.<p>
738     *
739     * @return the list of VFS resources that do not belong to this module
740     */
741    public List<String> getExcludeResources() {
742
743        return m_excluderesources;
744    }
745
746    /**
747     * Returns the list of explorer resource types that belong to this module.<p>
748     *
749     * @return the list of explorer resource types that belong to this module
750     */
751    public List<CmsExplorerTypeSettings> getExplorerTypes() {
752
753        return m_explorerTypeSettings;
754    }
755
756    /** Returns the export mode specified for the module.
757     * @return the module's export mode.
758     */
759    public ExportMode getExportMode() {
760
761        return m_exportMode;
762    }
763
764    /**
765     * Returns the list of export point added by this module.<p>
766     *
767     * @return the list of export point added by this module
768     */
769    public List<CmsExportPoint> getExportPoints() {
770
771        return m_exportPoints;
772    }
773
774    /**
775     * Returns the group name of this module.<p>
776     *
777     * @return the group name of this module
778     */
779    public String getGroup() {
780
781        return m_group;
782    }
783
784    /**
785     * Returns the importScript.<p>
786     *
787     * @return the importScript
788     */
789    public String getImportScript() {
790
791        return m_importScript;
792    }
793
794    /**
795     * Gets the import site.<p>
796     *
797     * If this is not empty, then it will be used as the site root for importing and exporting this module.<p>
798     *
799     * @return the import site
800     */
801    public String getImportSite() {
802
803        return m_importSite;
804    }
805
806    /**
807     * Returns the name of this module.<p>
808     *
809     * The module name must be a valid java package name.<p>
810     *
811     * @return the name of this module
812     */
813    public String getName() {
814
815        return m_name;
816    }
817
818    /**
819     * Returns the "nice" display name of this module.<p>
820     *
821     * @return the "nice" display name of this module
822     */
823    public String getNiceName() {
824
825        return m_niceName;
826    }
827
828    /**
829     * Gets the timestamp of this object's creation time.<p>
830     *
831     * @return the object creation timestamp
832     */
833    public long getObjectCreateTime() {
834
835        return m_objectCreateTime;
836    }
837
838    /**
839     * Returns a parameter value from the module parameters.<p>
840     *
841     * @param key the parameter to return the value for
842     * @return the parameter value from the module parameters
843     */
844    public String getParameter(String key) {
845
846        return m_parameters.get(key);
847    }
848
849    /**
850     * Returns a parameter value from the module parameters,
851     * or a given default value in case the parameter is not set.<p>
852     *
853     * @param key the parameter to return the value for
854     * @param defaultValue the default value in case there is no value stored for this key
855     * @return the parameter value from the module parameters
856     */
857    public String getParameter(String key, String defaultValue) {
858
859        String value = m_parameters.get(key);
860        return (value != null) ? value : defaultValue;
861    }
862
863    /**
864     * Returns the configured (immutable) module parameters.<p>
865     *
866     * @return the configured (immutable) module parameters
867     */
868    public SortedMap<String, String> getParameters() {
869
870        return m_parameters;
871    }
872
873    /**
874     * Returns the list of VFS resources that belong to this module.<p>
875     *
876     * @return the list of VFS resources that belong to this module
877     */
878    public List<String> getResources() {
879
880        return m_resources;
881    }
882
883    /**
884     * Returns the list of additional resource types that belong to this module.<p>
885     *
886     * @return the list of additional resource types that belong to this module
887     */
888    public List<I_CmsResourceType> getResourceTypes() {
889
890        return m_resourceTypes;
891    }
892
893    /**
894     * Returns the name of the user who uploaded this module.<p>
895     *
896     * @return the name of the user who uploaded this module
897     */
898    public String getUserInstalled() {
899
900        return m_userInstalled;
901    }
902
903    /**
904     * Returns the version of this module.<p>
905     *
906     * @return the version of this module
907     */
908    public CmsModuleVersion getVersion() {
909
910        return m_version;
911    }
912
913    /**
914     * @see java.lang.Object#hashCode()
915     */
916    @Override
917    public int hashCode() {
918
919        return m_name.hashCode();
920    }
921
922    /**
923     * Returns the createClassesFolder flag.<p>
924     *
925     * @return the createClassesFolder flag
926     */
927    public boolean isCreateClassesFolder() {
928
929        return m_createClassesFolder;
930    }
931
932    /**
933     * Returns the createElementsFolder flag.<p>
934     *
935     * @return the createElementsFolder flag
936     */
937    public boolean isCreateElementsFolder() {
938
939        return m_createElementsFolder;
940    }
941
942    /**
943     * Returns the createFormattersFolder flag.<p>
944     *
945     * @return the createFormattersFolder flag
946     */
947    public boolean isCreateFormattersFolder() {
948
949        return m_createFormattersFolder;
950    }
951
952    /**
953     * Returns the createLibFolder flag.<p>
954     *
955     * @return the createLibFolder flag
956     */
957    public boolean isCreateLibFolder() {
958
959        return m_createLibFolder;
960    }
961
962    /**
963     * Returns the createModuleFolder flag.<p>
964     *
965     * @return the createModuleFolder flag
966     */
967    public boolean isCreateModuleFolder() {
968
969        return m_createModuleFolder;
970    }
971
972    /**
973     * Returns the createResourcesFolder flag.<p>
974     *
975     * @return the createResourcesFolder flag
976     */
977    public boolean isCreateResourcesFolder() {
978
979        return m_createResourcesFolder;
980    }
981
982    /**
983     * Returns the createSchemasFolder flag.<p>
984     *
985     * @return the createSchemasFolder flag
986     */
987    public boolean isCreateSchemasFolder() {
988
989        return m_createSchemasFolder;
990    }
991
992    /**
993     * Returns the createTemplateFolder flag.<p>
994     *
995     * @return the createTemplateFolder flag
996     */
997    public boolean isCreateTemplateFolder() {
998
999        return m_createTemplateFolder;
1000    }
1001
1002    /**
1003     * Checks if this module is identical with another module.<p>
1004     *
1005     * Modules A, B are <b>identical</b> if <i>all</i> values of A are equal to B.
1006     * The values from {@link #getUserInstalled()} and {@link #getDateInstalled()}
1007     * are ignored for this test.<p>
1008     *
1009     * Modules A, B are <b>equal</b> if just the name of A is equal to the name of B.<p>
1010     *
1011     * @param other the module to compare with
1012     *
1013     * @return if the modules are identical
1014     *
1015     * @see #equals(Object)
1016     */
1017    public boolean isIdentical(CmsModule other) {
1018
1019        // some code redundancy here but this is easier to debug
1020        if (!isEqual(m_name, other.m_name)) {
1021            return false;
1022        }
1023        if (!isEqual(m_niceName, other.m_niceName)) {
1024            return false;
1025        }
1026        if (!isEqual(m_version, other.m_version)) {
1027            return false;
1028        }
1029        if (!isEqual(m_actionClass, other.m_actionClass)) {
1030            return false;
1031        }
1032        if (!isEqual(m_description, other.m_description)) {
1033            return false;
1034        }
1035        if (!isEqual(m_authorName, other.m_authorName)) {
1036            return false;
1037        }
1038        if (!isEqual(m_authorEmail, other.m_authorEmail)) {
1039            return false;
1040        }
1041        if (m_dateCreated != other.m_dateCreated) {
1042            return false;
1043        }
1044        return true;
1045    }
1046
1047    /** Checks, if the module should use the reduced export mode.
1048     * @return if reduce export mode should be used <code>true</code>, otherwise <code>false</code>.
1049     */
1050    public boolean isReducedExportMode() {
1051
1052        return ExportMode.REDUCED.equals(m_exportMode);
1053    }
1054
1055    /**
1056     * Sets the class name of this modules (optional) action class.<p>
1057     *
1058     * Providing <code>null</code> as a value indicates that this module does not use an action class.<p>
1059     *
1060     * <i>Please note:</i>It's not possible to set the action class name once the module
1061     * configuration has been frozen.<p>
1062     *
1063     * @param value the class name of this modules (optional) action class to set
1064     */
1065    public void setActionClass(String value) {
1066
1067        checkFrozen();
1068        if (CmsStringUtil.isEmpty(value)) {
1069            m_actionClass = null;
1070        } else {
1071            if (!CmsStringUtil.isValidJavaClassName(value)) {
1072                throw new CmsIllegalArgumentException(
1073                    Messages.get().container(Messages.ERR_MODULE_ACTION_CLASS_2, value, getName()));
1074            }
1075            m_actionClass = value;
1076        }
1077    }
1078
1079    /**
1080     * Sets the author email of this module.<p>
1081     *
1082     *
1083     * <i>Please note:</i>It's not possible to set the modules author email once the module
1084     * configuration has been frozen.<p>
1085     *
1086     * @param value the module description to set
1087     */
1088    public void setAuthorEmail(String value) {
1089
1090        checkFrozen();
1091        m_authorEmail = value.trim();
1092    }
1093
1094    /**
1095     * Sets the author name of this module.<p>
1096     *
1097     *
1098     * <i>Please note:</i>It's not possible to set the modules author name once the module
1099     * configuration has been frozen.<p>
1100     *
1101     * @param value the module description to set
1102     */
1103    public void setAuthorName(String value) {
1104
1105        checkFrozen();
1106        m_authorName = value.trim();
1107    }
1108
1109    /**
1110     * Sets the createClassesFolder flag.<p>
1111     *
1112     * @param createClassesFolder the createClassesFolder flag to set
1113     */
1114    public void setCreateClassesFolder(boolean createClassesFolder) {
1115
1116        m_createClassesFolder = createClassesFolder;
1117    }
1118
1119    /**
1120     * Sets the createElementsFolder flag.<p>
1121     *
1122     * @param createElementsFolder the createElementsFolder flag to set
1123     */
1124    public void setCreateElementsFolder(boolean createElementsFolder) {
1125
1126        m_createElementsFolder = createElementsFolder;
1127    }
1128
1129    /**
1130     * Sets the createFormattersFolder flag.<p>
1131     *
1132     * @param createFormattersFolder the createFormattersFolder flag to set
1133     */
1134    public void setCreateFormattersFolder(boolean createFormattersFolder) {
1135
1136        m_createFormattersFolder = createFormattersFolder;
1137    }
1138
1139    /**
1140     * Sets the createLibFolder flag.<p>
1141     *
1142     * @param createLibFolder the createLibFolder flag to set
1143     */
1144    public void setCreateLibFolder(boolean createLibFolder) {
1145
1146        m_createLibFolder = createLibFolder;
1147    }
1148
1149    /**
1150     * Sets the createModuleFolder flag.<p>
1151     *
1152     * @param createModuleFolder the createModuleFolder flag to set
1153     */
1154    public void setCreateModuleFolder(boolean createModuleFolder) {
1155
1156        m_createModuleFolder = createModuleFolder;
1157    }
1158
1159    /**
1160     * Sets the createResourcesFolder flag.<p>
1161     *
1162     * @param createResourcesFolder the createResourcesFolder flag to set
1163     */
1164    public void setCreateResourcesFolder(boolean createResourcesFolder) {
1165
1166        m_createResourcesFolder = createResourcesFolder;
1167    }
1168
1169    /**
1170     * Sets the createSchemasFolder flag .<p>
1171     *
1172     * @param createSchemasFolder the createSchemasFolder flag to set
1173     */
1174    public void setCreateSchemasFolder(boolean createSchemasFolder) {
1175
1176        m_createSchemasFolder = createSchemasFolder;
1177    }
1178
1179    /**
1180     * Sets the createTemplateFolder flag .<p>
1181     *
1182     * @param createTemplateFolder the createTemplateFolder flag to set
1183     */
1184    public void setCreateTemplateFolder(boolean createTemplateFolder) {
1185
1186        m_createTemplateFolder = createTemplateFolder;
1187    }
1188
1189    /**
1190     * Sets the date created of this module.<p>
1191     *
1192     *
1193     * <i>Please note:</i>It's not possible to set the module date created once the module
1194     * configuration has been frozen.<p>
1195     *
1196     * @param value the date created to set
1197     */
1198    public void setDateCreated(long value) {
1199
1200        checkFrozen();
1201        m_dateCreated = value;
1202    }
1203
1204    /**
1205     * Sets the installation date of this module.<p>
1206     *
1207     *
1208     * <i>Please note:</i>It's not possible to set the installation date once the module
1209     * configuration has been frozen.<p>
1210     *
1211     * @param value the installation date this module
1212     */
1213    public void setDateInstalled(long value) {
1214
1215        checkFrozen();
1216        m_dateInstalled = value;
1217    }
1218
1219    /**
1220     * Sets the list of module dependencies.<p>
1221     *
1222     * @param dependencies list of module dependencies
1223     */
1224    public void setDependencies(List<CmsModuleDependency> dependencies) {
1225
1226        checkFrozen();
1227        m_dependencies = dependencies;
1228    }
1229
1230    /**
1231     * Sets the description of this module.<p>
1232     *
1233     *
1234     * <i>Please note:</i>It's not possible to set the modules description once the module
1235     * configuration has been frozen.<p>
1236     *
1237     * @param value the module description to set
1238     */
1239    public void setDescription(String value) {
1240
1241        checkFrozen();
1242        m_description = value.trim();
1243    }
1244
1245    /**
1246     * Sets the resources excluded from this module.<p>
1247     *
1248     *
1249     * <i>Please note:</i>It's not possible to set the module resources once the module
1250     * configuration has been frozen.<p>
1251     *
1252     * @param value the resources to exclude from the module
1253     */
1254    public void setExcludeResources(List<String> value) {
1255
1256        checkFrozen();
1257        m_excluderesources = value;
1258    }
1259
1260    /**
1261     * Sets the additional explorer types that belong to this module.<p>
1262     *
1263     * @param explorerTypeSettings the explorer type settings.
1264     */
1265    public void setExplorerTypes(List<CmsExplorerTypeSettings> explorerTypeSettings) {
1266
1267        m_explorerTypeSettings = explorerTypeSettings;
1268    }
1269
1270    /**
1271     * Sets the export points of this module.<p>
1272     *
1273     * @param exportPoints the export points of this module.
1274     */
1275    public void setExportPoints(List<CmsExportPoint> exportPoints) {
1276
1277        m_exportPoints = exportPoints;
1278    }
1279
1280    /**
1281     * Sets the group name of this module.<p>
1282     *
1283     *
1284     * <i>Please note:</i>It's not possible to set the modules group name once the module
1285     * configuration has been frozen.<p>
1286     *
1287     * @param value the module group name to set
1288     */
1289    public void setGroup(String value) {
1290
1291        checkFrozen();
1292        m_group = value;
1293    }
1294
1295    /**
1296     * Sets the importScript.<p>
1297     *
1298     * @param importScript the importScript to set
1299     */
1300    public void setImportScript(String importScript) {
1301
1302        checkFrozen();
1303        m_importScript = importScript;
1304    }
1305
1306    /**
1307     * Sets the import site.<p>
1308     *
1309     * @param importSite the import site
1310     */
1311    public void setImportSite(String importSite) {
1312
1313        checkFrozen();
1314        if (importSite != null) {
1315            importSite = importSite.trim();
1316        }
1317        m_importSite = importSite;
1318    }
1319
1320    /**
1321     * Sets the name of this module.<p>
1322     *
1323     * The module name must be a valid java package name.<p>
1324     *
1325     * <i>Please note:</i>It's not possible to set the modules name once the module
1326     * configuration has been frozen.<p>
1327     *
1328     * @param value the module name to set
1329     */
1330    public void setName(String value) {
1331
1332        checkFrozen();
1333        if (!CmsStringUtil.isValidJavaClassName(value)) {
1334            throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_MODULE_NAME_1, value));
1335        }
1336        m_name = value;
1337    }
1338
1339    /**
1340     * Sets the "nice" display name of this module.<p>
1341     *
1342     * <i>Please note:</i>It's not possible to set the modules "nice" name once the module
1343     * configuration has been frozen.<p>
1344     *
1345     * @param value the "nice" display name of this module to set
1346     */
1347    public void setNiceName(String value) {
1348
1349        checkFrozen();
1350        if (CmsStringUtil.isEmptyOrWhitespaceOnly(value)) {
1351            m_niceName = getName();
1352        } else {
1353            m_niceName = value.trim();
1354        }
1355    }
1356
1357    /**
1358     * Sets the parameters of this module.<p>
1359     *
1360     *
1361     * <i>Please note:</i>It's not possible to set the module parameters once the module
1362     * configuration has been frozen.<p>
1363     *
1364     * @param parameters the module parameters to set
1365     */
1366    public void setParameters(SortedMap<String, String> parameters) {
1367
1368        checkFrozen();
1369        m_parameters = parameters;
1370    }
1371
1372    /** Set/unset the reduced export mode.
1373     * @param reducedExportMode if <code>true</code>, the export mode is set to {@link ExportMode#REDUCED}, otherwise to {@link ExportMode#DEFAULT}.
1374     */
1375    public void setReducedExportMode(boolean reducedExportMode) {
1376
1377        m_exportMode = reducedExportMode ? ExportMode.REDUCED : ExportMode.DEFAULT;
1378    }
1379
1380    /**
1381     * Sets the resources of this module.<p>
1382     *
1383     *
1384     * <i>Please note:</i>It's not possible to set the module resources once the module
1385     * configuration has been frozen.<p>
1386     *
1387     * @param value the module resources to set
1388     */
1389    public void setResources(List<String> value) {
1390
1391        checkFrozen();
1392        m_resources = value;
1393    }
1394
1395    /**
1396     * Sets the list of additional resource types that belong to this module.<p>
1397     *
1398     * @param resourceTypes list of additional resource types that belong to this module
1399     */
1400    public void setResourceTypes(List<I_CmsResourceType> resourceTypes) {
1401
1402        m_resourceTypes = Collections.unmodifiableList(resourceTypes);
1403    }
1404
1405    /**
1406     * Sets the user who installed of this module.<p>
1407     *
1408     *
1409     * <i>Please note:</i>It's not possible to set the user installed once the module
1410     * configuration has been frozen.<p>
1411     *
1412     * @param value the user who installed this module
1413     */
1414    public void setUserInstalled(String value) {
1415
1416        checkFrozen();
1417        m_userInstalled = value.trim();
1418    }
1419
1420    /**
1421     * Checks if this modules configuration is frozen.<p>
1422     *
1423     * @throws CmsIllegalArgumentException in case the configuration is already frozen
1424     */
1425    protected void checkFrozen() throws CmsIllegalArgumentException {
1426
1427        if (m_frozen) {
1428            throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_MODULE_FROZEN_1, getName()));
1429        }
1430    }
1431
1432    /**
1433     * Initializes this module, also freezing the module configuration.<p>
1434     *
1435     * @param cms an initialized OpenCms user context
1436     *
1437     * @throws CmsRoleViolationException if the given users does not have the <code>{@link CmsRole#DATABASE_MANAGER}</code> role
1438     */
1439    protected void initialize(CmsObject cms) throws CmsRoleViolationException {
1440
1441        checkFrozen();
1442        // check if the user has the required permissions
1443        OpenCms.getRoleManager().checkRole(cms, CmsRole.DATABASE_MANAGER);
1444
1445        m_frozen = true;
1446        m_resources = Collections.unmodifiableList(m_resources);
1447        m_excluderesources = Collections.unmodifiableList(m_excluderesources);
1448    }
1449
1450    /**
1451     * Sets the module action instance for this module.<p>
1452     *
1453     * @param actionInstance the module action instance for this module
1454     */
1455    /*package*/void setActionInstance(I_CmsModuleAction actionInstance) {
1456
1457        m_actionInstance = actionInstance;
1458
1459    }
1460
1461    /**
1462     * Resolves the module property "additionalresources" to the resource list and
1463     * vice versa.<p>
1464     *
1465     * This "special" module property is required as long as we do not have a new
1466     * GUI for editing of module resource entries. Once we have the new GUI, the
1467     * handling of "additionalresources" will be moved to the import of the module
1468     * and done only if the imported module is a 5.0 module.<p>
1469     */
1470    private void initOldAdditionalResources() {
1471
1472        SortedMap<String, String> parameters = new TreeMap<String, String>(m_parameters);
1473        List<String> resources = new ArrayList<String>(m_resources);
1474
1475        String additionalResources;
1476        additionalResources = parameters.get(MODULE_PROPERTY_ADDITIONAL_RESOURCES);
1477        if (additionalResources != null) {
1478            StringTokenizer tok = new StringTokenizer(
1479                additionalResources,
1480                MODULE_PROPERTY_ADDITIONAL_RESOURCES_SEPARATOR);
1481            while (tok.hasMoreTokens()) {
1482                String resource = tok.nextToken().trim();
1483                if ((!"-".equals(resource)) && (!resources.contains(resource))) {
1484                    resources.add(resource);
1485                }
1486            }
1487        }
1488
1489        m_resources = resources;
1490    }
1491
1492    /**
1493     * Checks if two objects are either both null, or equal.<p>
1494     *
1495     * @param a the first object to check
1496     * @param b the second object to check
1497     * @return true if the two object are either both null, or equal
1498     */
1499    private boolean isEqual(Object a, Object b) {
1500
1501        if (a == null) {
1502            return (b == null);
1503        }
1504        if (b == null) {
1505            return false;
1506        }
1507        return a.equals(b);
1508    }
1509}