/*
* This library is part of OpenCms -
* the Open Source Content Management System
*
* Copyright (c) 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, 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.xml.containerpage;
import org.opencms.file.CmsObject;
import org.opencms.file.CmsResource;
import org.opencms.file.types.CmsResourceTypeJsp;
import org.opencms.main.CmsException;
import org.opencms.main.CmsLog;
import org.opencms.main.OpenCms;
import org.opencms.security.CmsRole;
import org.opencms.util.CmsUUID;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
/**
* Represents a formatter configuration.<p>
*
* A formatter configuration can be either defined in the XML schema XSD of a XML content,
* or in a special sitemap configuration file.<p>
*
* @since 8.0.0
*/
public final class CmsFormatterConfiguration {
/** The empty formatter configuration. */
public static final CmsFormatterConfiguration EMPTY_CONFIGURATION = new CmsFormatterConfiguration(null, null);
/** The log instance for this class. */
public static final Log LOG = CmsLog.getLog(CmsFormatterConfiguration.class);
/** CmsObject used to read the JSP resources configured in the XSD schema. */
private static CmsObject m_adminCms;
/** All formatters that have been added to this configuration. */
private List<CmsFormatterBean> m_allFormatters;
/** Indicates if the preview formatter has already been calculated. */
private boolean m_previewCalculated;
/** The formatter that is to be used for the preview in the ADE gallery GUI. */
private CmsFormatterBean m_previewFormatter;
/** Simple lookup cache for the search content attribute. */
private Map<CmsUUID, Boolean> m_searchContent;
/** The formatters for different container types. */
private Map<String, CmsFormatterBean> m_typeFormatters;
/** The formatters for different widths. */
private List<CmsFormatterBean> m_widthFormatters;
/**
* Creates a new formatter configuration based on the given list of formatters.<p>
*
* @param cms the current users OpenCms context
* @param formatters the list of configured formatters
*/
private CmsFormatterConfiguration(CmsObject cms, List<CmsFormatterBean> formatters) {
if (formatters == null) {
// this is needed for the empty configuration
m_allFormatters = Collections.emptyList();
} else {
m_allFormatters = new ArrayList<CmsFormatterBean>(formatters);
}
m_widthFormatters = new ArrayList<CmsFormatterBean>(m_allFormatters.size());
m_typeFormatters = new HashMap<String, CmsFormatterBean>(m_allFormatters.size());
m_searchContent = new HashMap<CmsUUID, Boolean>();
init(cms, m_adminCms);
}
/**
* Returns the formatter configuration for the current project based on the given list of formatters.<p>
*
* @param cms the current users OpenCms context, required to know which project to read the JSP from
* @param formatters the list of configured formatters
*
* @return the formatter configuration for the current project based on the given list of formatters
*/
public static CmsFormatterConfiguration create(CmsObject cms, List<CmsFormatterBean> formatters) {
if ((formatters != null) && (formatters.size() > 0) && (cms != null)) {
return new CmsFormatterConfiguration(cms, formatters);
} else {
return EMPTY_CONFIGURATION;
}
}
/**
* Initialize the formatter configuration.<p>
*
* @param cms an initialized admin OpenCms user context
*
* @throws CmsException in case the initialization fails
*/
public static void initialize(CmsObject cms) throws CmsException {
OpenCms.getRoleManager().checkRole(cms, CmsRole.ADMINISTRATOR);
try {
// store the Admin cms to index Cms resources
m_adminCms = OpenCms.initCmsObject(cms);
m_adminCms.getRequestContext().setSiteRoot("");
} catch (CmsException e) {
// this should never happen
}
}
/**
* Gets a list of all defined formatters.<p>
*
* @return the list of all formatters
*/
public List<CmsFormatterBean> getAllFormatters() {
return new ArrayList<CmsFormatterBean>(m_allFormatters);
}
/**
* Selects the matching formatter for the provided type and width from this configuration.<p>
*
* This method first tries to find the formatter for the provided container type.
* If this fails, it returns the width based formatter that matched the container width.<p>
*
* @param containerType the container type
* @param containerWidth the container width
*
* @return the matching formatter, or <code>null</code> if none was found
*/
public CmsFormatterBean getFormatter(String containerType, int containerWidth) {
if (this == EMPTY_CONFIGURATION) {
// the empty configuration has no formatters
return null;
}
if (CmsFormatterBean.isPreviewType(containerType)) {
// the preview formatter was requested
return getPreviewFormatter();
}
CmsFormatterBean result = m_typeFormatters.get(containerType);
if ((result == null) && (containerWidth > 0)) {
// in case we don't have found a type and width info is set, check for width formatters
for (CmsFormatterBean f : m_widthFormatters) {
// iterate all width containers and see if we have a fit
if ((f.getMinWidth() <= containerWidth) && (containerWidth <= f.getMaxWidth())) {
// found a match
if ((result == null) || (result.getMinWidth() < f.getMinWidth())) {
result = f;
}
}
}
}
if (result == null) {
for (CmsFormatterBean f : m_widthFormatters) {
if (f.isMatchAll()) {
result = f;
}
}
}
return result;
}
/**
* Returns the formatter from this configuration that is to be used for the preview in the ADE gallery GUI,
* or <code>null</code> if there is no preview formatter configured.<p>
*
* @return the formatter from this configuration that is to be used for the preview in the ADE gallery GUI,
* or <code>null</code> if there is no preview formatter configured
*/
public CmsFormatterBean getPreviewFormatter() {
if (!m_previewCalculated) {
// preview formatter has not been calculated yet
CmsFormatterBean result = null;
if (this != EMPTY_CONFIGURATION) {
result = m_typeFormatters.get(CmsFormatterBean.PREVIEW_TYPE);
if (result == null) {
// empty configuration will always return null
result = getFormatter(CmsFormatterBean.WILDCARD_TYPE, CmsFormatterBean.PREVIEW_WIDTH);
// check the width formatter if we have a matching width for the preview window
if ((result == null) && !m_widthFormatters.isEmpty()) {
// no width is matching, see if we have one with a BIGGER width (preview will have scrollbars)
for (CmsFormatterBean f : m_widthFormatters) {
// iterate all width containers and see if we have a fit
if ((f.getMinWidth() >= CmsFormatterBean.PREVIEW_WIDTH)
&& (CmsFormatterBean.PREVIEW_WIDTH <= f.getMaxWidth())) {
// found a match
if ((result == null) || (result.getMinWidth() < f.getMinWidth())) {
result = f;
}
}
}
}
if ((result == null) && !m_typeFormatters.isEmpty()) {
// no luck with any width based formatter, let's just get the first type based formatter
result = m_typeFormatters.values().iterator().next();
}
}
}
m_previewCalculated = true;
m_previewFormatter = result;
}
return m_previewFormatter;
}
/**
* Returns the provided <code>true</code> in case this configuration has a formatter
* for the given type / width parameters.<p>
*
* @param containerType the container type
* @param containerWidth the container width
*
* @return the provided <code>true</code> in case this configuration has a formatter
* for the given type / width parameters.
*/
public boolean hasFormatter(String containerType, int containerWidth) {
return getFormatter(containerType, containerWidth) != null;
}
/**
* Returns <code>true</code> in case there is at least one usable formatter configured in this configuration.<p>
*
* @return <code>true</code> in case there is at least one usable formatter configured in this configuration
*/
public boolean hasFormatters() {
if (EMPTY_CONFIGURATION == this) {
return false;
}
return (m_typeFormatters.size() > 0) || (m_widthFormatters.size() > 0);
}
/**
* Returns <code>true</code> in case this configuration contains a formatter with the
* provided structure id that has been configured for including the formatted content in the online search.<p>
*
* @param formatterStructureId
*
* @return <code>true</code> in case this configuration contains a formatter with the
* provided structure id that has been configured for including the formatted content in the online search
*/
public boolean isSearchContent(CmsUUID formatterStructureId) {
if (EMPTY_CONFIGURATION == this) {
// don't search if this is just the empty configuration
return false;
}
// lookup the cache
Boolean result = m_searchContent.get(formatterStructureId);
if (result == null) {
// result so far unknown
for (CmsFormatterBean formatter : m_allFormatters) {
if (formatter.getJspStructureId().equals(formatterStructureId)) {
// found the match
result = Boolean.valueOf(formatter.isSearchContent());
// first match rules
break;
}
}
if (result == null) {
// no match found, in this case dont search the content
result = Boolean.FALSE;
}
// store result in the cache
m_searchContent.put(formatterStructureId, result);
}
return result.booleanValue();
}
/**
* Initializes all formatters of this configuration.<p>
*
* It is also checked if the configured JSP root path exists, if not the formatter is removed
* as it is unusable.<p>
*
* @param userCms the current users OpenCms context, used for selecting the right project
* @param adminCms the Admin user context to use for reading the JSP resources
*/
private void init(CmsObject userCms, CmsObject adminCms) {
for (CmsFormatterBean formatter : m_allFormatters) {
if (formatter.getJspStructureId() == null) {
// a formatter may have been re-used so the structure id is already available
CmsResource res = null;
// first we make sure that the JSP exists at all (and also we read the UUID that way)
try {
// first get a cms copy so we can mess up the context without modifying the original
CmsObject cmsCopy = OpenCms.initCmsObject(adminCms);
cmsCopy.getRequestContext().setCurrentProject(userCms.getRequestContext().getCurrentProject());
// switch to the root site
cmsCopy.getRequestContext().setSiteRoot("");
// now read the JSP
res = cmsCopy.readResource(formatter.getJspRootPath());
} catch (CmsException e) {
//if this happens the result is null and we write a LOG error
}
if ((res == null) || !CmsResourceTypeJsp.isJsp(res)) {
// the formatter must exist and it must be a JSP
LOG.error(Messages.get().getBundle().key(
Messages.ERR_FORMATTER_JSP_DONT_EXIST_1,
formatter.getJspRootPath()));
} else {
formatter.setJspStructureId(res.getStructureId());
// res may still be null in case of failure
}
}
if (formatter.getJspStructureId() != null) {
// if no structure id is available then the formatter JSP root path is invalid
String oldUri = null;
Object key;
if (formatter.isTypeFormatter()) {
String type = formatter.getContainerType();
key = type;
CmsFormatterBean oldFormatter = m_typeFormatters.get(type);
if (oldFormatter != null) {
oldUri = oldFormatter.getJspRootPath();
}
m_typeFormatters.put(type, formatter);
} else {
Integer minWidth = Integer.valueOf(formatter.getMinWidth());
key = minWidth;
int old = m_widthFormatters.lastIndexOf(formatter);
if (old >= 0) {
oldUri = m_widthFormatters.remove(old).getJspRootPath();
}
m_widthFormatters.add(formatter);
}
if (formatter.isPreviewFormatter()) {
// this is a preview formatter, last one overwrites earlier one
m_previewFormatter = formatter;
}
if (oldUri != null) {
LOG.warn(Messages.get().getBundle().key(
Messages.LOG_CONTENT_DEFINITION_DUPLICATE_FORMATTER_4,
new Object[] {key, oldUri, formatter.getJspRootPath(), formatter.getLocation()}));
}
}
}
m_allFormatters = Collections.unmodifiableList(m_allFormatters);
m_typeFormatters = Collections.unmodifiableMap(m_typeFormatters);
m_widthFormatters = Collections.unmodifiableList(m_widthFormatters);
}
}