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, 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.ade.configuration.formatters; 029 030import org.opencms.gwt.shared.CmsGwtConstants; 031import org.opencms.main.CmsLog; 032import org.opencms.util.CmsUUID; 033import org.opencms.xml.content.CmsXmlContentProperty; 034 035import java.util.ArrayList; 036import java.util.Collections; 037import java.util.HashMap; 038import java.util.LinkedHashMap; 039import java.util.List; 040import java.util.Map; 041import java.util.concurrent.ExecutionException; 042 043import org.apache.commons.logging.Log; 044 045import com.google.common.cache.CacheBuilder; 046import com.google.common.cache.CacheLoader; 047import com.google.common.cache.LoadingCache; 048import com.google.common.collect.ImmutableList; 049 050/** 051 * Contains the setting-related data for a formatter bean. 052 */ 053public class CmsSettingConfiguration { 054 055 /** The logger instance for this class. */ 056 private static final Log LOG = CmsLog.getLog(CmsSettingConfiguration.class); 057 058 /** Cache for calculating and storing the setting definition maps for various combinations of override shared setting configuration file ids. */ 059 private LoadingCache<ImmutableList<CmsUUID>, Map<String, CmsXmlContentProperty>> m_cache = CacheBuilder.newBuilder().concurrencyLevel( 060 4).build(new CacheLoader<ImmutableList<CmsUUID>, Map<String, CmsXmlContentProperty>>() { 061 062 @SuppressWarnings("synthetic-access") 063 @Override 064 public Map<String, CmsXmlContentProperty> load(ImmutableList<CmsUUID> sharedSettingOverrides) 065 throws Exception { 066 067 return resolveSettings(sharedSettingOverrides); 068 069 } 070 071 }); 072 073 /** The display type. */ 074 private String m_displayType; 075 076 /** The key of the formatter using this configuration (may be null). */ 077 private String m_formatterKey; 078 079 /** The settings configured in the formatter configuration. */ 080 private List<CmsXmlContentProperty> m_listedSettings; 081 082 /** A map of all shared setting configurations in the system, with their structure ids as keys. */ 083 private Map<CmsUUID, Map<CmsSharedSettingKey, CmsXmlContentProperty>> m_sharedSettingConfigsById; 084 085 /** The list of structure ids of shared settings files configured in the formatter. The last entry has the highest priority. */ 086 private List<CmsUUID> m_sharedSettingsIdsFromFormatter; 087 088 /** 089 * Creates an empty configuration. 090 */ 091 public CmsSettingConfiguration() { 092 093 m_listedSettings = new ArrayList<>(); 094 m_sharedSettingConfigsById = new HashMap<>(); 095 m_displayType = null; 096 m_sharedSettingsIdsFromFormatter = new ArrayList<>(); 097 } 098 099 /** 100 * 101 * @param listedSettings the setting entries configured in the formatter configuration 102 * @param sharedSettingConfigsById the map of shared setting configurations, with their structure ids as keys 103 * @param includeIds the list of structure ids of shared setting configurations referenced from the formatter configuration 104 * @param formatterKey the key of the formatter using this setting configuration (may be null) 105 * @param displayType the display type 106 */ 107 public CmsSettingConfiguration( 108 List<CmsXmlContentProperty> listedSettings, 109 Map<CmsUUID, Map<CmsSharedSettingKey, CmsXmlContentProperty>> sharedSettingConfigsById, 110 List<CmsUUID> includeIds, 111 String formatterKey, 112 String displayType) { 113 114 m_listedSettings = listedSettings; 115 m_sharedSettingConfigsById = sharedSettingConfigsById; 116 m_displayType = displayType; 117 m_formatterKey = formatterKey; 118 m_sharedSettingsIdsFromFormatter = new ArrayList<>(includeIds); 119 } 120 121 /** 122 * Gets the setting map by looking up the configured settings' include names in either the shared settings files 123 * configured in the formatter configuration, or the override shared settings files whose ids are passed as parameter. 124 * 125 * <p> 126 * Setting definitions from Override shared settings files have higher priority than those referenced in the formatter 127 * configuration, and later entries in both lists have higher prioritiy than earlier ones. 128 * 129 * @param sharedSettingOverrides the structure ids of Override shared setting configurations (highest priority last) 130 * 131 * @return the setting definition map 132 */ 133 public Map<String, CmsXmlContentProperty> getSettings(ImmutableList<CmsUUID> sharedSettingOverrides) { 134 135 try { 136 return m_cache.get(sharedSettingOverrides); 137 } catch (ExecutionException e) { 138 LOG.error(e.getLocalizedMessage(), e); 139 return Collections.emptyMap(); 140 } 141 } 142 143 /** 144 * Combines shard setting definitions from multiple shared setting files into a single map. 145 * 146 * @param ids the structure ids of shared setting files, in order of increasing specificity 147 * 148 * @return the combined map of shared setting definitions 149 */ 150 private Map<CmsSharedSettingKey, CmsXmlContentProperty> combineSharedSettingDefinitionMaps(List<CmsUUID> ids) { 151 152 Map<CmsSharedSettingKey, CmsXmlContentProperty> result = new HashMap<>(); 153 for (CmsUUID settingFileId : ids) { 154 Map<CmsSharedSettingKey, CmsXmlContentProperty> sharedSettingsForLevel = m_sharedSettingConfigsById.get( 155 settingFileId); 156 if (sharedSettingsForLevel != null) { 157 // since we have different map keys for each (includeName, formatterKey) combination, 158 // putAll does the right thing here, i.e. setting definitions are overridden for their individual formatter keys 159 result.putAll(sharedSettingsForLevel); 160 } else { 161 LOG.warn("Shared setting reference not found: " + settingFileId); 162 } 163 } 164 return result; 165 } 166 167 /** 168 * Helper method to get a shared setting for this formatter. 169 * 170 * <p>Prioritizes shared settings with a formatter key matching this formatter's key. 171 * 172 * @param map the map of shared settings 173 * @param includeName the effective include name of the setting definition to find 174 * 175 * @return the shared setting definition 176 */ 177 private CmsXmlContentProperty getSharedSetting( 178 Map<CmsSharedSettingKey, CmsXmlContentProperty> map, 179 String includeName) { 180 181 CmsXmlContentProperty result = null; 182 183 // try formatter key specific entry first, if not found try the general entry 184 185 if (m_formatterKey != null) { 186 result = map.get(new CmsSharedSettingKey(includeName, m_formatterKey)); 187 } 188 if (result == null) { 189 result = map.get(new CmsSharedSettingKey(includeName, null)); 190 } 191 return result; 192 193 } 194 195 /** 196 * Computes the finished map of settings for the given combination of shared setting overrides.+ 197 * 198 * @param overrideSharedSettingsIds the structure ids of shared setting overrides active in the current context, with the most specific override last 199 * 200 * @return the finished map of settings 201 */ 202 private Map<String, CmsXmlContentProperty> resolveSettings(ImmutableList<CmsUUID> overrideSharedSettingsIds) { 203 204 Map<String, CmsXmlContentProperty> result = new LinkedHashMap<>(); 205 206 Map<CmsSharedSettingKey, CmsXmlContentProperty> sharedSettingDefinitions = combineSharedSettingDefinitionMaps( 207 m_sharedSettingsIdsFromFormatter); 208 Map<CmsSharedSettingKey, CmsXmlContentProperty> overrideSettingDefinitions = combineSharedSettingDefinitionMaps( 209 overrideSharedSettingsIds); 210 211 /* 212 * For each setting listed in the formatter, try to find a matching setting definition in both the shared settings (referenced from the formatter) 213 * and the setting overrides (configured in the sitemap/master configuration). These three setting definition objects (or less, if the setting override or shared 214 * setting doesn't exist) are then merged, such that each individual field value for the setting definition is pulled from the first entry in the list 215 * [overrideSetting, settingFromFormatter, settingFromSharedSettings] where it is defined. I.e. the field values defined in setting overrides have the highest 216 * priority. 217 */ 218 for (CmsXmlContentProperty settingDef : m_listedSettings) { 219 String includeName = settingDef.getIncludeName(settingDef.getName()); 220 if (includeName == null) { 221 continue; 222 } 223 224 CmsXmlContentProperty defaultSetting = getSharedSetting(sharedSettingDefinitions, includeName); 225 CmsXmlContentProperty overrideSetting = getSharedSetting(overrideSettingDefinitions, includeName); 226 CmsXmlContentProperty mergedSetting = settingDef; 227 if (defaultSetting != null) { 228 mergedSetting = mergedSetting.mergeDefaults(defaultSetting); 229 } 230 if (overrideSetting != null) { 231 mergedSetting = overrideSetting.mergeDefaults(mergedSetting); 232 } 233 if (mergedSetting.getName() == null) { 234 continue; 235 } 236 result.put(mergedSetting.getName(), mergedSetting); 237 } 238 if ((m_displayType != null) && !result.containsKey(CmsFormatterBeanParser.SETTING_DISPLAY_TYPE)) { 239 CmsXmlContentProperty displayType = new CmsXmlContentProperty( 240 CmsFormatterBeanParser.SETTING_DISPLAY_TYPE, 241 "string", 242 CmsGwtConstants.HIDDEN_SETTINGS_WIDGET_NAME, 243 null, 244 null, 245 null, 246 m_displayType, 247 null, 248 null, 249 null, 250 null); 251 result.put(displayType.getName(), displayType); 252 } 253 return Collections.unmodifiableMap(result); 254 } 255}