/*
 * File   : $Source: /usr/local/cvs/opencms/src/org/opencms/jsp/decorator/CmsDecorationDefintion.java,v $
 * Date   : $Date: 2008-03-14 14:29:56 $
 * Version: $Revision: 1.6 $
 *
 * This library is part of OpenCms -
 * the Open Source Content Management System
 *
 * Copyright (c) 2002 - 2008 Alkacon Software GmbH (http://www.alkacon.com)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * For further information about Alkacon Software GmbH, please see the
 * company website: http://www.alkacon.com
 *
 * For further information about OpenCms, please see the
 * project website: http://www.opencms.org
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package org.opencms.jsp.decorator;

import org.opencms.cache.CmsVfsMemoryObjectCache;
import org.opencms.file.CmsObject;
import org.opencms.file.CmsResource;
import org.opencms.file.CmsResourceFilter;
import org.opencms.file.types.CmsResourceTypePlain;
import org.opencms.main.CmsException;
import org.opencms.main.CmsLog;
import org.opencms.main.OpenCms;
import org.opencms.module.CmsModule;
import org.opencms.util.CmsStringUtil;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;

import org.apache.commons.logging.Log;

/**
 * This class defines text decoration to be made by the postprocessor.<p>
 * 
 * @author Michael Emmerich  
 * 
 * @version $Revision: 1.6 $ 
 * 
 * @since 6.1.3 
 */
public class CmsDecorationDefintion {

    /** The log object for this class. */
    private static final Log LOG = CmsLog.getLog(CmsDecorationDefintion.class);

    /** The name of the configuration file holding all word substitutions. */
    private String m_configurationFile;

    /** Flag, signaling if the first occurance of a word must be marked differntly. */
    private boolean m_markFirst;

    /** The name of the substitution. */
    private String m_name;

    /** The post to be added after the occurance of a word. */
    private String m_postText;

    /** The post to be added after the occurance of a word on its first occurance. */
    private String m_postTextFirst;

    /** The prefix to be added in front of the occurance of a word. */
    private String m_preText;

    /** The prefix to be added in front of the occurance of a word on its first occurance. */
    private String m_preTextFirst;

    /**
     * Constructor, creates a new empty CmsDecorationDefintion.<p>
     */
    public CmsDecorationDefintion() {

        m_configurationFile = null;
        m_markFirst = false;
        m_name = null;
        m_postText = null;
        m_postTextFirst = null;
        m_preText = null;
        m_preTextFirst = null;
    }

    /**
     * Constructor, creates a new CmsDecorationDefintion with given values.<p>
     * 
     * @param name the name of the decoration defintinion
     * @param preText the preText to be used
     * @param postText the postText to be used
     * @param preTextFirst the preTextFirst to be used
     * @param postTextFirst the postTextFirst to be used
     * @param markFrist the flag to use different decorations for the first occurance
     * @param configurationFile the name of the configuration file
     */
    public CmsDecorationDefintion(
        String name,
        String preText,
        String postText,
        String preTextFirst,
        String postTextFirst,
        boolean markFrist,
        String configurationFile) {

        m_configurationFile = configurationFile;
        m_markFirst = markFrist;
        m_name = name;
        m_postText = postText;
        m_postTextFirst = postTextFirst;
        m_preText = preText;
        m_preTextFirst = preTextFirst;
    }

    /**
     * Returns all different decoration configuration names (like "abbr" or "acronym") that 
     * are in the config file pointed to by module parameter "configfile".<p>
     * 
     * @param cms needed to access the decoration definition XML content
     * 
     * @return  all different decoration configuration names (like "abbr" or "acronym") that 
     *      are in the config file pointed to by module parameter "configfile"
     *      
     * @throws CmsException if sth goes wrong
     */
    public static List getDecorationDefinitionNames(CmsObject cms) throws CmsException {

        List result = new ArrayList();
        CmsModule module = OpenCms.getModuleManager().getModule("com.alkacon.opencms.extendeddecorator");
        String configFile = module.getParameter("configfile");
        if (CmsStringUtil.isEmpty(configFile)) {
            LOG.error(Messages.get().getBundle().key(Messages.LOG_ERROR_CONFIG_MISSING_0));
        } else {
            CmsDecoratorConfiguration config = new CmsDecoratorConfiguration(cms, configFile);
            List decorationDefinitions = config.getDecorationDefinitions();
            Iterator it = decorationDefinitions.iterator();
            CmsDecorationDefintion decDef;
            while (it.hasNext()) {
                decDef = (CmsDecorationDefintion)it.next();
                result.add(decDef.getName());
            }

        }

        return result;
    }

    /**
     * Creates a CmsDecorationBundle of text decoration to be used by the decorator.<p>
     * 
     * @param cms the CmsObject
     * @param locale the locale to build the decoration bundle for. If no locale is given, a bundle of all locales is build
     * @return CmsDecorationBundle including all decoration lists that match the locale
     * @throws CmsException if something goes wrong
     */
    public CmsDecorationBundle createDecorationBundle(CmsObject cms, Locale locale) throws CmsException {

        // get configfile basename and the list of all decoration map files
        List decorationMapFiles = getDecorationMapFiles(cms);
        if (LOG.isDebugEnabled()) {
            LOG.debug(Messages.get().getBundle().key(
                Messages.LOG_DECORATION_DEFINITION_MAP_FILES_2,
                decorationMapFiles,
                locale));
        }

        // create decoration maps
        List decorationMaps = getDecorationMaps(cms, decorationMapFiles);
        if (LOG.isDebugEnabled()) {
            LOG.debug(Messages.get().getBundle().key(Messages.LOG_DECORATION_DEFINITION_MAPS_2, decorationMaps, locale));
        }

        // now that we have all decoration maps we can build the decoration bundle
        // the bundele is depending on the locale, if a locale is given, only those decoration maps that contain the
        // locale (or no locale at all) must be used. If no locale is given, all decoration maps are 
        // put into the decoration bundle        
        return createDecorationBundle(decorationMaps, locale);
    }

    /**
     * Creates a CmsDecorationBundle of text decoration to be used by the decorator based on a list of decoration maps.<p>
     * 
     * @param decorationMaps the decoration maps to build the bundle from
     * @param locale the locale to build the decoration bundle for. If no locale is given, a bundle of all locales is build
     * @return CmsDecorationBundle including all decoration lists that match the locale
     */
    public CmsDecorationBundle createDecorationBundle(List decorationMaps, Locale locale) {

        CmsDecorationBundle decorationBundle = new CmsDecorationBundle(locale);
        // sort the bundles
        Collections.sort(decorationMaps);
        // now process the decoration maps to see which of those must be added to the bundle
        Iterator i = decorationMaps.iterator();
        while (i.hasNext()) {
            CmsDecorationMap decMap = (CmsDecorationMap)i.next();
            // a decoration map must be added to the bundle if one of the following conditions match:
            // 1) the bundle has no locale
            // 2) the bundle has a locale and the locale of the map is equal or a sublocale
            // 3) the bundle has a locale and the map has no locale
            if ((locale == null)
                || ((decMap.getLocale() == null))
                || (locale.getDisplayLanguage().equals(decMap.getLocale().getDisplayLanguage()))) {
                decorationBundle.putAll(decMap.getDecorationMap());
                if (LOG.isDebugEnabled()) {
                    LOG.debug(Messages.get().getBundle().key(
                        Messages.LOG_DECORATION_DEFINITION_CREATE_BUNDLE_2,
                        decMap.getName(),
                        locale));
                }
            }
        }
        return decorationBundle;
    }

    /**
     * Returns the configurationFile.<p>
     * 
     *
     * @return the configurationFile
     */
    public String getConfigurationFile() {

        return m_configurationFile;
    }

    /**
     * Returns the name.<p>
     *
     * @return the name
     */
    public String getName() {

        return m_name;
    }

    /**
     * Returns the postText.<p>
     *
     * @return the postText
     */
    public String getPostText() {

        return m_postText;
    }

    /**
     * Returns the postTextFirst.<p>
     *
     * @return the postTextFirst
     */
    public String getPostTextFirst() {

        return m_postTextFirst;
    }

    /**
     * Returns the preText.<p>
     *
     * @return the preText
     */
    public String getPreText() {

        return m_preText;
    }

    /**
     * Returns the preTextFirst.<p>
     *
     * @return the preTextFirst
     */
    public String getPreTextFirst() {

        return m_preTextFirst;
    }

    /**
     * Returns the markFirst flag.<p>
     *
     * @return the markFirst flag
     */
    public boolean isMarkFirst() {

        return m_markFirst;
    }

    /**
     * Sets the configurationFile.<p>
     *
     * @param configurationFile the configurationFile to set
     */
    public void setConfigurationFile(String configurationFile) {

        m_configurationFile = configurationFile;
    }

    /**
     * Sets the markFirst flag.<p>
     *
     * @param markFirst the markFirst flag to set
     */
    public void setMarkFirst(boolean markFirst) {

        m_markFirst = markFirst;
    }

    /**
     * Sets the name.<p>
     *
     * @param name the name to set
     */
    public void setName(String name) {

        m_name = name;
    }

    /**
     * Sets the postText.<p>
     *
     * @param postText the postText to set
     */
    public void setPostText(String postText) {

        m_postText = postText;
    }

    /**
     * Sets the postTextFirst.<p>
     *
     * @param postTextFirst the postTextFirst to set
     */
    public void setPostTextFirst(String postTextFirst) {

        m_postTextFirst = postTextFirst;
    }

    /**
     * Sets the preText.<p>
     *
     * @param preText the preText to set
     */
    public void setPreText(String preText) {

        m_preText = preText;
    }

    /**
     * Sets the preTextFirst.<p>
     *
     * @param preTextFirst the preTextFirst to set
     */
    public void setPreTextFirst(String preTextFirst) {

        m_preTextFirst = preTextFirst;
    }

    /**
     * @see java.lang.Object#toString()
     */
    public String toString() {

        StringBuffer buf = new StringBuffer();
        buf.append(this.getClass().getName());
        buf.append(" [name = '");
        buf.append(m_name);
        buf.append("', markFirst = '");
        buf.append(m_markFirst);
        buf.append("', preText = '");
        buf.append(m_preText);
        buf.append("', postText = '");
        buf.append(m_postText);
        buf.append("', preTextFirst = '");
        buf.append(m_preTextFirst);
        buf.append("', postTextFirst = '");
        buf.append(m_postTextFirst);
        buf.append("', configFile = ");
        buf.append(m_configurationFile);
        buf.append("]");
        return buf.toString();
    }

    /**
     * Gets the list of all decoartion map files that match to the current basename.<p>
     * 
     * @param cms the CmsObject
     * @return list of CmsResources of the decoration map files
     * @throws CmsException if something goes wrong.
     */
    private List getDecorationMapFiles(CmsObject cms) throws CmsException {

        List files = new ArrayList();

        // calcualte the basename for the decoration map files
        // the basename is the filename without the fileextension and any "_locale" postfixes
        // e.g. decoration_en.csv will generate "decoration" as basename
        StringBuffer baseFilename = new StringBuffer();
        baseFilename.append(CmsResource.getParentFolder(m_configurationFile));
        String filename = cms.readResource(m_configurationFile).getName();
        // get rid of the fileextension if there is one
        if (filename.lastIndexOf(".") > -1) {
            filename = filename.substring(0, filename.lastIndexOf("."));
        }
        // extract the basename
        if (filename.lastIndexOf("_") > -1) {
            filename = filename.substring(0, filename.lastIndexOf("_"));
        }
        baseFilename.append(filename);
        String basename = baseFilename.toString();

        // get all config files which belong to this basename

        List resources = cms.readResources(CmsResource.getParentFolder(m_configurationFile), CmsResourceFilter.DEFAULT);
        Iterator i = resources.iterator();
        while (i.hasNext()) {
            CmsResource res = (CmsResource)i.next();
            if (cms.getSitePath(res).startsWith(basename)
                && (res.getTypeId() == CmsResourceTypePlain.getStaticTypeId())) {
                files.add(res);
            }
        }

        return files;
    }

    /**
     * Creates a list of decoration map objects from a given list of decoration files.<p>
     * 
     * @param cms the CmsObject
     * @param decorationListFiles the list of decoration files
     * @return list of decoration map objects
     */
    private List getDecorationMaps(CmsObject cms, List decorationListFiles) {

        List decorationMaps = new ArrayList();
        Iterator i = decorationListFiles.iterator();
        while (i.hasNext()) {
            CmsResource res = (CmsResource)i.next();
            try {
                CmsDecorationMap decMap = (CmsDecorationMap)CmsVfsMemoryObjectCache.getVfsMemoryObjectCache().getCachedObject(
                    cms,
                    res.getRootPath());
                if (decMap == null) {
                    decMap = new CmsDecorationMap(cms, res, this);
                    CmsVfsMemoryObjectCache.getVfsMemoryObjectCache().putCachedObject(cms, res.getRootPath(), decMap);
                }

                decorationMaps.add(decMap);
            } catch (CmsException e) {
                if (LOG.isErrorEnabled()) {
                    LOG.error(Messages.get().getBundle().key(
                        Messages.LOG_DECORATION_DEFINITION_CREATE_MAP_2,
                        res.getName(),
                        e));
                }
            }
        }
        return decorationMaps;
    }

}
