org.opencms.i18n.tools.CmsContainerPageCopier.java Source code

Java tutorial

Introduction

Here is the source code for org.opencms.i18n.tools.CmsContainerPageCopier.java

Source

/*
 * This library is part of OpenCms -
 * the Open Source Content Management System
 *
 * Copyright (c) Alkacon Software GmbH & Co. KG (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, 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.i18n.tools;

import org.opencms.ade.configuration.CmsADEConfigData;
import org.opencms.ade.configuration.CmsResourceTypeConfig;
import org.opencms.file.CmsFile;
import org.opencms.file.CmsObject;
import org.opencms.file.CmsProperty;
import org.opencms.file.CmsPropertyDefinition;
import org.opencms.file.CmsResource;
import org.opencms.file.CmsResourceFilter;
import org.opencms.file.types.CmsResourceTypeFolder;
import org.opencms.file.types.CmsResourceTypeXmlContainerPage;
import org.opencms.file.types.I_CmsResourceType;
import org.opencms.i18n.CmsLocaleGroupService;
import org.opencms.i18n.CmsLocaleGroupService.Status;
import org.opencms.i18n.CmsLocaleManager;
import org.opencms.loader.I_CmsFileNameGenerator;
import org.opencms.lock.CmsLockActionRecord;
import org.opencms.lock.CmsLockActionRecord.LockChange;
import org.opencms.lock.CmsLockUtil;
import org.opencms.main.CmsException;
import org.opencms.main.CmsLog;
import org.opencms.main.OpenCms;
import org.opencms.site.CmsSite;
import org.opencms.ui.Messages;
import org.opencms.util.CmsFileUtil;
import org.opencms.util.CmsMacroResolver;
import org.opencms.util.CmsStringUtil;
import org.opencms.util.CmsUUID;
import org.opencms.xml.CmsXmlException;
import org.opencms.xml.containerpage.CmsContainerBean;
import org.opencms.xml.containerpage.CmsContainerElementBean;
import org.opencms.xml.containerpage.CmsContainerPageBean;
import org.opencms.xml.containerpage.CmsXmlContainerPage;
import org.opencms.xml.containerpage.CmsXmlContainerPageFactory;
import org.opencms.xml.content.CmsXmlContent;
import org.opencms.xml.content.CmsXmlContentFactory;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.apache.commons.logging.Log;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

/**
 * Helper class for copying container pages including some of their elements.<p>
 */
public class CmsContainerPageCopier {

    /**
     * Enum representing the element copy mode.<p>
     */
    public enum CopyMode {
        /** Choose between reuse / copy automatically depending on source / target locale and the configuration .*/
        automatic,

        /** Do not copy elements. */
        reuse,

        /** Automatically determine when to copy elements. */
        smartCopy,

        /** Like smartCopy, but also converts locales of copied elements. */
        smartCopyAndChangeLocale;

    }

    /**
     * Exception indicating that no custom replacement element was found
     * for a type which requires replacement.<p>
     */
    public static class NoCustomReplacementException extends Exception {

        /** Serial version id. */
        private static final long serialVersionUID = 1L;

        /** The resource for which no exception was found. */
        private CmsResource m_resource;

        /**
         * Creates a new instance.<p>
         *
         * @param resource the resource for which no replacement was found
         */
        public NoCustomReplacementException(CmsResource resource) {
            super();
            m_resource = resource;
        }

        /**
         * Gets the resource for which no replacement was found.<p>
         *
         * @return the resource
         */
        public CmsResource getResource() {

            return m_resource;
        }
    }

    /** The log instance used for this class. */
    private static final Log LOG = CmsLog.getLog(CmsContainerPageCopier.class);

    /** The CMS context used by this object. */
    private CmsObject m_cms;

    /** The copied resource. */
    private CmsResource m_copiedFolderOrPage;

    /** The copy mode. */
    private CopyMode m_copyMode = CopyMode.smartCopyAndChangeLocale;

    /** List of created resources. */
    private List<CmsResource> m_createdResources = Lists.newArrayList();

    /** Map of custom replacements. */
    private Map<CmsUUID, CmsUUID> m_customReplacements;

    /** Maps structure ids of original container elements to structure ids of their copies/replacements. */
    private Map<CmsUUID, CmsUUID> m_elementReplacements = Maps.newHashMap();

    /** The original page. */
    private CmsResource m_originalPage;

    /** The target folder. */
    private CmsResource m_targetFolder;

    /** Resource types which require custom replacements. */
    private Set<String> m_typesWithRequiredReplacements;

    /**
     * Creates a new instance.<p>
     *
     * @param cms the CMS context to use
     */
    public CmsContainerPageCopier(CmsObject cms) {
        m_cms = cms;
    }

    /**
     * Converts locales for the copied container element.<p>
     *
     * @param elementResource the copied container element
     * @throws CmsException if something goes wrong
     */
    public void adjustLocalesForElement(CmsResource elementResource) throws CmsException {

        if (m_copyMode != CopyMode.smartCopyAndChangeLocale) {
            return;
        }

        CmsFile file = m_cms.readFile(elementResource);
        Locale oldLocale = OpenCms.getLocaleManager().getDefaultLocale(m_cms, m_originalPage);
        Locale newLocale = OpenCms.getLocaleManager().getDefaultLocale(m_cms, m_targetFolder);
        CmsXmlContent content = CmsXmlContentFactory.unmarshal(m_cms, file);
        try {
            content.moveLocale(oldLocale, newLocale);
            LOG.info(
                    "Replacing locale " + oldLocale + " -> " + newLocale + " for " + elementResource.getRootPath());
            file.setContents(content.marshal());
            m_cms.writeFile(file);
        } catch (CmsXmlException e) {
            LOG.info("NOT replacing locale for " + elementResource.getRootPath() + ": old=" + oldLocale + ", new="
                    + newLocale + ", contentLocales=" + content.getLocales());
        }

    }

    /**
     * Gets the copied folder or page.<p>
     *
     * @return the copied folder or page
     */
    public CmsResource getCopiedFolderOrPage() {

        return m_copiedFolderOrPage;
    }

    /**
     * Returns the target folder.<p>
     *
     * @return the target folder
     */
    public CmsResource getTargetFolder() {

        return m_targetFolder;
    }

    /**
     * Produces the replacement for a container page element to use in a copy of an existing container page.<p>
     *
     * @param targetPage the target container page
     * @param originalElement the original element
     * @return the replacement element for the copied page
     *
     * @throws CmsException if something goes wrong
     * @throws NoCustomReplacementException if a custom replacement is not found for a type which requires it
     */
    public CmsContainerElementBean replaceContainerElement(CmsResource targetPage,
            CmsContainerElementBean originalElement) throws CmsException, NoCustomReplacementException {
        // if (m_elementReplacements.containsKey(originalElement.getId()

        CmsObject targetCms = OpenCms.initCmsObject(m_cms);

        CmsSite site = OpenCms.getSiteManager().getSiteForRootPath(m_targetFolder.getRootPath());
        if (site != null) {
            targetCms.getRequestContext().setSiteRoot(site.getSiteRoot());
        }

        if ((originalElement.getFormatterId() == null) || (originalElement.getId() == null)) {
            String rootPath = m_originalPage != null ? m_originalPage.getRootPath() : "???";
            LOG.warn("Skipping container element because of missing id in page: " + rootPath);
            return null;
        }

        if (m_elementReplacements.containsKey(originalElement.getId())) {
            return new CmsContainerElementBean(m_elementReplacements.get(originalElement.getId()),
                    maybeReplaceFormatter(originalElement.getFormatterId()),
                    maybeReplaceFormatterInSettings(originalElement.getIndividualSettings()),
                    originalElement.isCreateNew());
        } else {
            CmsResource originalResource = m_cms.readResource(originalElement.getId(),
                    CmsResourceFilter.IGNORE_EXPIRATION);
            I_CmsResourceType type = OpenCms.getResourceManager().getResourceType(originalResource);
            CmsADEConfigData config = OpenCms.getADEManager().lookupConfiguration(m_cms, targetPage.getRootPath());
            CmsResourceTypeConfig typeConfig = config.getResourceType(type.getTypeName());
            if ((m_copyMode != CopyMode.reuse) && (typeConfig != null)
                    && (originalElement.isCreateNew() || typeConfig.isCopyInModels())
                    && !type.getTypeName().equals(CmsResourceTypeXmlContainerPage.MODEL_GROUP_TYPE_NAME)) {
                CmsResource resourceCopy = typeConfig.createNewElement(targetCms, originalResource,
                        targetPage.getRootPath());
                CmsContainerElementBean copy = new CmsContainerElementBean(resourceCopy.getStructureId(),
                        maybeReplaceFormatter(originalElement.getFormatterId()),
                        maybeReplaceFormatterInSettings(originalElement.getIndividualSettings()),
                        originalElement.isCreateNew());
                m_elementReplacements.put(originalElement.getId(), resourceCopy.getStructureId());
                LOG.info("Copied container element " + originalResource.getRootPath() + " -> "
                        + resourceCopy.getRootPath());
                CmsLockActionRecord record = null;
                try {
                    record = CmsLockUtil.ensureLock(m_cms, resourceCopy);
                    adjustLocalesForElement(resourceCopy);
                } finally {
                    if ((record != null) && (record.getChange() == LockChange.locked)) {
                        m_cms.unlockResource(resourceCopy);
                    }
                }
                return copy;
            } else if (m_customReplacements != null) {
                CmsUUID replacementId = m_customReplacements.get(originalElement.getId());
                if (replacementId != null) {

                    return new CmsContainerElementBean(replacementId,
                            maybeReplaceFormatter(originalElement.getFormatterId()),
                            maybeReplaceFormatterInSettings(originalElement.getIndividualSettings()),
                            originalElement.isCreateNew());
                } else {
                    if ((m_typesWithRequiredReplacements != null)
                            && m_typesWithRequiredReplacements.contains(type.getTypeName())) {
                        throw new NoCustomReplacementException(originalResource);
                    } else {
                        return originalElement;
                    }

                }
            } else {
                LOG.info("Reusing container element: " + originalResource.getRootPath());
                return originalElement;

            }
        }
    }

    /**
     * Replaces the elements in the copied container page with copies, if appropriate based on the current copy mode.<p>
     *
     * @param containerPage the container page copy whose elements should be replaced with copies
     *
     * @throws CmsException if something goes wrong
     * @throws NoCustomReplacementException if a custom replacement element was not found for a type which requires it
     */
    public void replaceElements(CmsResource containerPage) throws CmsException, NoCustomReplacementException {

        CmsObject rootCms = OpenCms.initCmsObject(m_cms);
        rootCms.getRequestContext().setSiteRoot("");
        CmsObject targetCms = OpenCms.initCmsObject(m_cms);
        targetCms.getRequestContext().setSiteRoot("");
        CmsSite site = OpenCms.getSiteManager().getSiteForRootPath(m_targetFolder.getRootPath());
        if (site != null) {
            targetCms.getRequestContext().setSiteRoot(site.getSiteRoot());
        } else if (OpenCms.getSiteManager().startsWithShared(m_targetFolder.getRootPath())) {
            targetCms.getRequestContext().setSiteRoot(OpenCms.getSiteManager().getSharedFolder());
        }

        CmsProperty elementReplacementProp = rootCms.readPropertyObject(m_targetFolder,
                CmsPropertyDefinition.PROPERTY_ELEMENT_REPLACEMENTS, true);
        if ((elementReplacementProp != null) && (elementReplacementProp.getValue() != null)) {
            try {
                CmsResource elementReplacementMap = targetCms.readResource(elementReplacementProp.getValue(),
                        CmsResourceFilter.IGNORE_EXPIRATION);
                OpenCms.getLocaleManager();
                String encoding = CmsLocaleManager.getResourceEncoding(targetCms, elementReplacementMap);
                CmsFile elementReplacementFile = targetCms.readFile(elementReplacementMap);
                Properties props = new Properties();
                props.load(new InputStreamReader(new ByteArrayInputStream(elementReplacementFile.getContents()),
                        encoding));
                CmsMacroResolver resolver = new CmsMacroResolver();
                resolver.addMacro("sourcesite", m_cms.getRequestContext().getSiteRoot().replaceAll("/+$", ""));
                resolver.addMacro("targetsite", targetCms.getRequestContext().getSiteRoot().replaceAll("/+$", ""));
                Map<CmsUUID, CmsUUID> customReplacements = Maps.newHashMap();
                for (Map.Entry<Object, Object> entry : props.entrySet()) {
                    if ((entry.getKey() instanceof String) && (entry.getValue() instanceof String)) {
                        try {
                            String key = (String) entry.getKey();
                            if ("required".equals(key)) {
                                m_typesWithRequiredReplacements = Sets
                                        .newHashSet(((String) entry.getValue()).split(" *, *"));
                                continue;
                            }
                            key = resolver.resolveMacros(key);
                            String value = (String) entry.getValue();
                            value = resolver.resolveMacros(value);
                            CmsResource keyRes = rootCms.readResource(key, CmsResourceFilter.IGNORE_EXPIRATION);
                            CmsResource valRes = rootCms.readResource(value, CmsResourceFilter.IGNORE_EXPIRATION);
                            customReplacements.put(keyRes.getStructureId(), valRes.getStructureId());
                        } catch (Exception e) {
                            LOG.error(e.getLocalizedMessage(), e);
                        }
                        m_customReplacements = customReplacements;
                    }
                }
            } catch (CmsException e) {
                LOG.warn(e.getLocalizedMessage(), e);
            } catch (IOException e) {
                LOG.warn(e.getLocalizedMessage(), e);
            }
        }

        CmsXmlContainerPage pageXml = CmsXmlContainerPageFactory.unmarshal(m_cms, containerPage);
        CmsContainerPageBean page = pageXml.getContainerPage(m_cms);
        List<CmsContainerBean> newContainers = Lists.newArrayList();
        for (CmsContainerBean container : page.getContainers().values()) {
            List<CmsContainerElementBean> newElements = Lists.newArrayList();
            for (CmsContainerElementBean element : container.getElements()) {
                CmsContainerElementBean newBean = replaceContainerElement(containerPage, element);
                if (newBean != null) {
                    newElements.add(newBean);
                }
            }
            CmsContainerBean newContainer = new CmsContainerBean(container.getName(), container.getType(),
                    container.getParentInstanceId(), container.isRootContainer(), newElements);
            newContainers.add(newContainer);
        }
        CmsContainerPageBean newPageBean = new CmsContainerPageBean(newContainers);
        pageXml.save(rootCms, newPageBean);
    }

    /**
     * Starts the page copying process.<p>
     *
     * @param source the source (can be either a container page, or a folder whose default file is a container page)
     * @param target the target folder
     *
     * @throws CmsException if soemthing goes wrong
     * @throws NoCustomReplacementException if a custom replacement element was not found
     */
    public void run(CmsResource source, CmsResource target) throws CmsException, NoCustomReplacementException {

        run(source, target, null);
    }

    /**
     * Starts the page copying process.<p>
     *
     * @param source the source (can be either a container page, or a folder whose default file is a container page)
     * @param target the target folder
     * @param targetName the name to give the new folder
     *
     * @throws CmsException if soemthing goes wrong
     * @throws NoCustomReplacementException if a custom replacement element was not found
     */
    public void run(CmsResource source, CmsResource target, String targetName)
            throws CmsException, NoCustomReplacementException {

        LOG.info("Starting page copy process: page='" + source.getRootPath() + "', targetFolder='"
                + target.getRootPath() + "'");
        CmsObject rootCms = OpenCms.initCmsObject(m_cms);
        rootCms.getRequestContext().setSiteRoot("");
        if (m_copyMode == CopyMode.automatic) {
            Locale sourceLocale = OpenCms.getLocaleManager().getDefaultLocale(rootCms, source);
            Locale targetLocale = OpenCms.getLocaleManager().getDefaultLocale(rootCms, target);
            // if same locale, copy elements, otherwise use configured setting
            LOG.debug("copy mode automatic: source=" + sourceLocale + " target=" + targetLocale + " reuseConfig="
                    + OpenCms.getLocaleManager().shouldReuseElements() + "");
            if (sourceLocale.equals(targetLocale)) {
                m_copyMode = CopyMode.smartCopyAndChangeLocale;
            } else {
                if (OpenCms.getLocaleManager().shouldReuseElements()) {
                    m_copyMode = CopyMode.reuse;
                } else {
                    m_copyMode = CopyMode.smartCopyAndChangeLocale;
                }
            }
        }

        if (source.isFolder()) {
            if (source.equals(target)) {
                throw new CmsException(Messages.get().container(Messages.ERR_PAGECOPY_SOURCE_IS_TARGET_0));
            }
            CmsResource page = m_cms.readDefaultFile(source, CmsResourceFilter.IGNORE_EXPIRATION);
            if ((page == null) || !CmsResourceTypeXmlContainerPage.isContainerPage(page)) {
                throw new CmsException(Messages.get().container(Messages.ERR_PAGECOPY_INVALID_PAGE_0));
            }
            List<CmsProperty> properties = Lists.newArrayList(m_cms.readPropertyObjects(source, false));
            Iterator<CmsProperty> iterator = properties.iterator();
            while (iterator.hasNext()) {
                CmsProperty prop = iterator.next();
                // copied folder may be root of a locale subtree, but since we may want to copy to a different locale,
                // we don't want the locale property in the copy
                if (prop.getName().equals(CmsPropertyDefinition.PROPERTY_LOCALE)
                        || prop.getName().equals(CmsPropertyDefinition.PROPERTY_ELEMENT_REPLACEMENTS)) {
                    iterator.remove();
                }
            }

            I_CmsFileNameGenerator nameGen = OpenCms.getResourceManager().getNameGenerator();
            String copyPath;
            if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(targetName)) {
                copyPath = CmsStringUtil.joinPaths(target.getRootPath(), targetName);
                if (rootCms.existsResource(copyPath)) {
                    copyPath = nameGen.getNewFileName(rootCms, copyPath + "%(number)", 4, true);
                }
            } else {
                copyPath = CmsFileUtil
                        .removeTrailingSeparator(CmsStringUtil.joinPaths(target.getRootPath(), source.getName()));
                copyPath = nameGen.getNewFileName(rootCms, copyPath + "%(number)", 4, true);
            }
            Double maxNavPosObj = readMaxNavPos(target);
            double maxNavpos = maxNavPosObj == null ? 0 : maxNavPosObj.doubleValue();
            boolean hasNavpos = maxNavPosObj != null;
            CmsResource copiedFolder = rootCms.createResource(copyPath,
                    OpenCms.getResourceManager().getResourceType(CmsResourceTypeFolder.RESOURCE_TYPE_NAME), null,
                    properties);
            m_createdResources.add(copiedFolder);
            if (hasNavpos) {
                String newNavPosStr = "" + (maxNavpos + 10);
                rootCms.writePropertyObject(copiedFolder.getRootPath(),
                        new CmsProperty(CmsPropertyDefinition.PROPERTY_NAVPOS, newNavPosStr, null));
            }
            String pageCopyPath = CmsStringUtil.joinPaths(copiedFolder.getRootPath(), page.getName());
            m_originalPage = page;
            m_targetFolder = target;
            m_copiedFolderOrPage = copiedFolder;
            rootCms.copyResource(page.getRootPath(), pageCopyPath);

            CmsResource copiedPage = rootCms.readResource(pageCopyPath, CmsResourceFilter.IGNORE_EXPIRATION);

            m_createdResources.add(copiedPage);
            replaceElements(copiedPage);
            CmsLocaleGroupService localeGroupService = rootCms.getLocaleGroupService();
            if (Status.linkable == localeGroupService.checkLinkable(m_originalPage, copiedPage)) {
                try {
                    localeGroupService.attachLocaleGroupIndirect(m_originalPage, copiedPage);
                } catch (CmsException e) {
                    LOG.error(e.getLocalizedMessage(), e);
                }
            }
            tryUnlock(copiedFolder);
        } else {
            CmsResource page = source;
            if (!CmsResourceTypeXmlContainerPage.isContainerPage(page)) {
                throw new CmsException(Messages.get().container(Messages.ERR_PAGECOPY_INVALID_PAGE_0));
            }
            I_CmsFileNameGenerator nameGen = OpenCms.getResourceManager().getNameGenerator();
            String copyPath = CmsFileUtil
                    .removeTrailingSeparator(CmsStringUtil.joinPaths(target.getRootPath(), source.getName()));
            int lastDot = copyPath.lastIndexOf(".");
            int lastSlash = copyPath.lastIndexOf("/");
            if (lastDot > lastSlash) { // path has an extension
                String macroPath = copyPath.substring(0, lastDot) + "%(number)" + copyPath.substring(lastDot);
                copyPath = nameGen.getNewFileName(rootCms, macroPath, 4, true);
            } else {
                copyPath = nameGen.getNewFileName(rootCms, copyPath + "%(number)", 4, true);
            }
            Double maxNavPosObj = readMaxNavPos(target);
            double maxNavpos = maxNavPosObj == null ? 0 : maxNavPosObj.doubleValue();
            boolean hasNavpos = maxNavPosObj != null;
            rootCms.copyResource(page.getRootPath(), copyPath);
            if (hasNavpos) {
                String newNavPosStr = "" + (maxNavpos + 10);
                rootCms.writePropertyObject(copyPath,
                        new CmsProperty(CmsPropertyDefinition.PROPERTY_NAVPOS, newNavPosStr, null));
            }
            CmsResource copiedPage = rootCms.readResource(copyPath);
            m_createdResources.add(copiedPage);
            m_originalPage = page;
            m_targetFolder = target;
            m_copiedFolderOrPage = copiedPage;
            replaceElements(copiedPage);
            CmsLocaleGroupService localeGroupService = rootCms.getLocaleGroupService();
            if (Status.linkable == localeGroupService.checkLinkable(m_originalPage, copiedPage)) {
                try {
                    localeGroupService.attachLocaleGroupIndirect(m_originalPage, copiedPage);
                } catch (CmsException e) {
                    LOG.error(e.getLocalizedMessage(), e);
                }
            }
            tryUnlock(copiedPage);

        }
    }

    /**
     * Sets the copy mode.<p>
     *
     * @param copyMode the copy mode
     */
    public void setCopyMode(CopyMode copyMode) {

        m_copyMode = copyMode;
    }

    /**
     * Reads the max nav position from the contents of a folder.<p>
     *
     * @param target a folder
     * @return the maximal NavPos from the contents of the folder, or null if no resources with a valid NavPos were found in the folder
     *
     * @throws CmsException if something goes wrong
     */
    Double readMaxNavPos(CmsResource target) throws CmsException {

        List<CmsResource> existingResourcesInFolder = m_cms.readResources(target,
                CmsResourceFilter.IGNORE_EXPIRATION, false);

        double maxNavpos = 0.0;
        boolean hasNavpos = false;
        for (CmsResource existingResource : existingResourcesInFolder) {
            CmsProperty navpos = m_cms.readPropertyObject(existingResource, CmsPropertyDefinition.PROPERTY_NAVPOS,
                    false);
            if (navpos.getValue() != null) {
                try {
                    double navposNum = Double.parseDouble(navpos.getValue());
                    hasNavpos = true;
                    maxNavpos = Math.max(navposNum, maxNavpos);
                } catch (NumberFormatException e) {
                    // ignore
                }
            }
        }
        if (hasNavpos) {
            return Double.valueOf(maxNavpos);
        } else {
            return null;
        }
    }

    /**
     * Uses the custom translation table to translate formatter id.<p>
     *
     * @param formatterId the formatter id
     * @return the formatter replacement
     */
    private CmsUUID maybeReplaceFormatter(CmsUUID formatterId) {

        if (m_customReplacements != null) {
            CmsUUID replacement = m_customReplacements.get(formatterId);
            if (replacement != null) {
                return replacement;
            }
        }
        return formatterId;
    }

    /**
     * Replaces formatter id in element settings.<p>
     *
     * @param individualSettings the settings in which to replace the formatter id
     *
     * @return the map with the possible replaced ids
     */
    private Map<String, String> maybeReplaceFormatterInSettings(Map<String, String> individualSettings) {

        if (individualSettings == null) {
            return null;
        } else if (m_customReplacements == null) {
            return individualSettings;
        } else {
            LinkedHashMap<String, String> result = new LinkedHashMap<String, String>();
            for (Map.Entry<String, String> entry : individualSettings.entrySet()) {
                String value = entry.getValue();
                if (CmsUUID.isValidUUID(value)) {
                    CmsUUID valueId = new CmsUUID(value);
                    if (m_customReplacements.containsKey(valueId)) {
                        value = "" + m_customReplacements.get(valueId);
                    }
                }
                result.put(entry.getKey(), value);
            }
            return result;
        }
    }

    /**
     * Tries to unlock the given resource.<p>
     *
     * @param resource the resource to unlock
     */
    private void tryUnlock(CmsResource resource) {

        try {
            m_cms.unlockResource(resource);
        } catch (CmsException e) {
            // usually not a problem
            LOG.debug("failed to unlock " + resource.getRootPath(), e);
        }

    }

}