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.jsp.decorator;
029
030import org.opencms.cache.CmsVfsMemoryObjectCache;
031import org.opencms.file.CmsObject;
032import org.opencms.file.CmsResource;
033import org.opencms.file.CmsResourceFilter;
034import org.opencms.file.types.CmsResourceTypePlain;
035import org.opencms.loader.CmsLoaderException;
036import org.opencms.main.CmsException;
037import org.opencms.main.CmsLog;
038import org.opencms.main.OpenCms;
039import org.opencms.module.CmsModule;
040import org.opencms.util.CmsStringUtil;
041
042import java.util.ArrayList;
043import java.util.Collections;
044import java.util.Iterator;
045import java.util.List;
046import java.util.Locale;
047
048import org.apache.commons.logging.Log;
049
050/**
051 * This class defines text decoration to be made by the postprocessor.<p>
052 *
053 * @since 6.1.3
054 */
055public class CmsDecorationDefintion {
056
057    /** The log object for this class. */
058    private static final Log LOG = CmsLog.getLog(CmsDecorationDefintion.class);
059
060    /** The name of the configuration file holding all word substitutions. */
061    private String m_configurationFile;
062
063    /** Flag, signaling if the first occurance of a word must be marked differntly. */
064    private boolean m_markFirst;
065
066    /** The name of the substitution. */
067    private String m_name;
068
069    /** The post to be added after the occurance of a word. */
070    private String m_postText;
071
072    /** The post to be added after the occurance of a word on its first occurance. */
073    private String m_postTextFirst;
074
075    /** The prefix to be added in front of the occurance of a word. */
076    private String m_preText;
077
078    /** The prefix to be added in front of the occurance of a word on its first occurance. */
079    private String m_preTextFirst;
080
081    /**
082     * Constructor, creates a new empty CmsDecorationDefintion.<p>
083     */
084    public CmsDecorationDefintion() {
085
086        m_configurationFile = null;
087        m_markFirst = false;
088        m_name = null;
089        m_postText = null;
090        m_postTextFirst = null;
091        m_preText = null;
092        m_preTextFirst = null;
093    }
094
095    /**
096     * Constructor, creates a new CmsDecorationDefintion with given values.<p>
097     *
098     * @param name the name of the decoration defintinion
099     * @param preText the preText to be used
100     * @param postText the postText to be used
101     * @param preTextFirst the preTextFirst to be used
102     * @param postTextFirst the postTextFirst to be used
103     * @param markFrist the flag to use different decorations for the first occurance
104     * @param configurationFile the name of the configuration file
105     */
106    public CmsDecorationDefintion(
107        String name,
108        String preText,
109        String postText,
110        String preTextFirst,
111        String postTextFirst,
112        boolean markFrist,
113        String configurationFile) {
114
115        m_configurationFile = configurationFile;
116        m_markFirst = markFrist;
117        m_name = name;
118        m_postText = postText;
119        m_postTextFirst = postTextFirst;
120        m_preText = preText;
121        m_preTextFirst = preTextFirst;
122    }
123
124    /**
125     * Returns all different decoration configuration names (like "abbr" or "acronym") that
126     * are in the config file pointed to by module parameter "configfile".<p>
127     *
128     * @param cms needed to access the decoration definition XML content
129     *
130     * @return  all different decoration configuration names (like "abbr" or "acronym") that
131     *      are in the config file pointed to by module parameter "configfile"
132     *
133     * @throws CmsException if sth goes wrong
134     */
135    public static List<String> getDecorationDefinitionNames(CmsObject cms) throws CmsException {
136
137        List<String> result = new ArrayList<String>();
138        CmsModule module = OpenCms.getModuleManager().getModule("com.alkacon.opencms.extendeddecorator");
139        String configFile = module.getParameter("configfile");
140        if (CmsStringUtil.isEmpty(configFile)) {
141            LOG.error(Messages.get().getBundle().key(Messages.LOG_ERROR_CONFIG_MISSING_0));
142        } else {
143            CmsDecoratorConfiguration config = new CmsDecoratorConfiguration(cms, configFile);
144            List<CmsDecorationDefintion> decorationDefinitions = config.getDecorationDefinitions();
145            Iterator<CmsDecorationDefintion> it = decorationDefinitions.iterator();
146            CmsDecorationDefintion decDef;
147            while (it.hasNext()) {
148                decDef = it.next();
149                result.add(decDef.getName());
150            }
151
152        }
153
154        return result;
155    }
156
157    /**
158     * Creates a CmsDecorationBundle of text decoration to be used by the decorator.<p>
159     *
160     * @param cms the CmsObject
161     * @param locale the locale to build the decoration bundle for. If no locale is given, a bundle of all locales is build
162     * @return CmsDecorationBundle including all decoration lists that match the locale
163     * @throws CmsException if something goes wrong
164     */
165    public CmsDecorationBundle createDecorationBundle(CmsObject cms, Locale locale) throws CmsException {
166
167        // get configfile basename and the list of all decoration map files
168        List<CmsResource> decorationMapFiles = getDecorationMapFiles(cms);
169        if (LOG.isDebugEnabled()) {
170            LOG.debug(
171                Messages.get().getBundle().key(
172                    Messages.LOG_DECORATION_DEFINITION_MAP_FILES_2,
173                    decorationMapFiles,
174                    locale));
175        }
176
177        // create decoration maps
178        List<CmsDecorationMap> decorationMaps = getDecorationMaps(cms, decorationMapFiles);
179        if (LOG.isDebugEnabled()) {
180            LOG.debug(
181                Messages.get().getBundle().key(Messages.LOG_DECORATION_DEFINITION_MAPS_2, decorationMaps, locale));
182        }
183
184        // now that we have all decoration maps we can build the decoration bundle
185        // the bundele is depending on the locale, if a locale is given, only those decoration maps that contain the
186        // locale (or no locale at all) must be used. If no locale is given, all decoration maps are
187        // put into the decoration bundle
188        return createDecorationBundle(decorationMaps, locale);
189    }
190
191    /**
192     * Creates a CmsDecorationBundle of text decoration to be used by the decorator based on a list of decoration maps.<p>
193     *
194     * @param decorationMaps the decoration maps to build the bundle from
195     * @param locale the locale to build the decoration bundle for. If no locale is given, a bundle of all locales is build
196     * @return CmsDecorationBundle including all decoration lists that match the locale
197     */
198    public CmsDecorationBundle createDecorationBundle(List<CmsDecorationMap> decorationMaps, Locale locale) {
199
200        CmsDecorationBundle decorationBundle = new CmsDecorationBundle(locale);
201        // sort the bundles
202        Collections.sort(decorationMaps);
203        // now process the decoration maps to see which of those must be added to the bundle
204        Iterator<CmsDecorationMap> i = decorationMaps.iterator();
205        while (i.hasNext()) {
206            CmsDecorationMap decMap = i.next();
207            // a decoration map must be added to the bundle if one of the following conditions match:
208            // 1) the bundle has no locale
209            // 2) the bundle has a locale and the locale of the map is equal or a sublocale
210            // 3) the bundle has a locale and the map has no locale
211            if ((locale == null)
212                || ((decMap.getLocale() == null))
213                || (locale.getDisplayLanguage().equals(decMap.getLocale().getDisplayLanguage()))) {
214                decorationBundle.putAll(decMap.getDecorationMap());
215                if (LOG.isDebugEnabled()) {
216                    LOG.debug(
217                        Messages.get().getBundle().key(
218                            Messages.LOG_DECORATION_DEFINITION_CREATE_BUNDLE_2,
219                            decMap.getName(),
220                            locale));
221                }
222            }
223        }
224        return decorationBundle;
225    }
226
227    /**
228     * Returns the configurationFile.<p>
229     *
230     *
231     * @return the configurationFile
232     */
233    public String getConfigurationFile() {
234
235        return m_configurationFile;
236    }
237
238    /**
239     * Returns the name.<p>
240     *
241     * @return the name
242     */
243    public String getName() {
244
245        return m_name;
246    }
247
248    /**
249     * Returns the postText.<p>
250     *
251     * @return the postText
252     */
253    public String getPostText() {
254
255        return m_postText;
256    }
257
258    /**
259     * Returns the postTextFirst.<p>
260     *
261     * @return the postTextFirst
262     */
263    public String getPostTextFirst() {
264
265        return m_postTextFirst;
266    }
267
268    /**
269     * Returns the preText.<p>
270     *
271     * @return the preText
272     */
273    public String getPreText() {
274
275        return m_preText;
276    }
277
278    /**
279     * Returns the preTextFirst.<p>
280     *
281     * @return the preTextFirst
282     */
283    public String getPreTextFirst() {
284
285        return m_preTextFirst;
286    }
287
288    /**
289     * Returns the markFirst flag.<p>
290     *
291     * @return the markFirst flag
292     */
293    public boolean isMarkFirst() {
294
295        return m_markFirst;
296    }
297
298    /**
299     * Sets the configurationFile.<p>
300     *
301     * @param configurationFile the configurationFile to set
302     */
303    public void setConfigurationFile(String configurationFile) {
304
305        m_configurationFile = configurationFile;
306    }
307
308    /**
309     * Sets the markFirst flag.<p>
310     *
311     * @param markFirst the markFirst flag to set
312     */
313    public void setMarkFirst(boolean markFirst) {
314
315        m_markFirst = markFirst;
316    }
317
318    /**
319     * Sets the name.<p>
320     *
321     * @param name the name to set
322     */
323    public void setName(String name) {
324
325        m_name = name;
326    }
327
328    /**
329     * Sets the postText.<p>
330     *
331     * @param postText the postText to set
332     */
333    public void setPostText(String postText) {
334
335        m_postText = postText;
336    }
337
338    /**
339     * Sets the postTextFirst.<p>
340     *
341     * @param postTextFirst the postTextFirst to set
342     */
343    public void setPostTextFirst(String postTextFirst) {
344
345        m_postTextFirst = postTextFirst;
346    }
347
348    /**
349     * Sets the preText.<p>
350     *
351     * @param preText the preText to set
352     */
353    public void setPreText(String preText) {
354
355        m_preText = preText;
356    }
357
358    /**
359     * Sets the preTextFirst.<p>
360     *
361     * @param preTextFirst the preTextFirst to set
362     */
363    public void setPreTextFirst(String preTextFirst) {
364
365        m_preTextFirst = preTextFirst;
366    }
367
368    /**
369     * @see java.lang.Object#toString()
370     */
371    @Override
372    public String toString() {
373
374        StringBuffer buf = new StringBuffer();
375        buf.append(this.getClass().getName());
376        buf.append(" [name = '");
377        buf.append(m_name);
378        buf.append("', markFirst = '");
379        buf.append(m_markFirst);
380        buf.append("', preText = '");
381        buf.append(m_preText);
382        buf.append("', postText = '");
383        buf.append(m_postText);
384        buf.append("', preTextFirst = '");
385        buf.append(m_preTextFirst);
386        buf.append("', postTextFirst = '");
387        buf.append(m_postTextFirst);
388        buf.append("', configFile = ");
389        buf.append(m_configurationFile);
390        buf.append("]");
391        return buf.toString();
392    }
393
394    /**
395     * Gets the list of all decoartion map files that match to the current basename.<p>
396     *
397     * @param cms the CmsObject
398     * @return list of CmsResources of the decoration map files
399     * @throws CmsException if something goes wrong.
400     */
401    private List<CmsResource> getDecorationMapFiles(CmsObject cms) throws CmsException {
402
403        List<CmsResource> files = new ArrayList<CmsResource>();
404
405        // calcualte the basename for the decoration map files
406        // the basename is the filename without the fileextension and any "_locale" postfixes
407        // e.g. decoration_en.csv will generate "decoration" as basename
408        StringBuffer baseFilename = new StringBuffer();
409        baseFilename.append(CmsResource.getParentFolder(m_configurationFile));
410        String filename = cms.readResource(m_configurationFile).getName();
411        // get rid of the fileextension if there is one
412        if (filename.lastIndexOf(".") > -1) {
413            filename = filename.substring(0, filename.lastIndexOf("."));
414        }
415        // extract the basename
416        if (filename.lastIndexOf("_") > -1) {
417            filename = filename.substring(0, filename.lastIndexOf("_"));
418        }
419        baseFilename.append(filename);
420        String basename = baseFilename.toString();
421
422        // get all config files which belong to this basename
423        int plainId;
424        try {
425            plainId = OpenCms.getResourceManager().getResourceType(
426                CmsResourceTypePlain.getStaticTypeName()).getTypeId();
427        } catch (CmsLoaderException e) {
428            // this should really never happen
429            plainId = CmsResourceTypePlain.getStaticTypeId();
430        }
431        List<CmsResource> resources = cms.readResources(
432            CmsResource.getParentFolder(m_configurationFile),
433            CmsResourceFilter.DEFAULT);
434        Iterator<CmsResource> i = resources.iterator();
435        while (i.hasNext()) {
436            CmsResource res = i.next();
437            if (cms.getSitePath(res).startsWith(basename) && (res.getTypeId() == plainId)) {
438                files.add(res);
439            }
440        }
441
442        return files;
443    }
444
445    /**
446     * Creates a list of decoration map objects from a given list of decoration files.<p>
447     *
448     * @param cms the CmsObject
449     * @param decorationListFiles the list of decoration files
450     * @return list of decoration map objects
451     */
452    private List<CmsDecorationMap> getDecorationMaps(CmsObject cms, List<CmsResource> decorationListFiles) {
453
454        List<CmsDecorationMap> decorationMaps = new ArrayList<CmsDecorationMap>();
455        Iterator<CmsResource> i = decorationListFiles.iterator();
456        while (i.hasNext()) {
457            CmsResource res = i.next();
458            try {
459                CmsDecorationMap decMap = (CmsDecorationMap)CmsVfsMemoryObjectCache.getVfsMemoryObjectCache().getCachedObject(
460                    cms,
461                    res.getRootPath());
462                if (decMap == null) {
463                    decMap = new CmsDecorationMap(cms, res, this);
464                    CmsVfsMemoryObjectCache.getVfsMemoryObjectCache().putCachedObject(cms, res.getRootPath(), decMap);
465                }
466
467                decorationMaps.add(decMap);
468            } catch (CmsException e) {
469                if (LOG.isErrorEnabled()) {
470                    LOG.error(
471                        Messages.get().getBundle().key(
472                            Messages.LOG_DECORATION_DEFINITION_CREATE_MAP_2,
473                            res.getName(),
474                            e));
475                }
476            }
477        }
478        return decorationMaps;
479    }
480
481}