001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (C) Alkacon Software (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.xml.containerpage; 029 030import org.opencms.cache.CmsVfsMemoryObjectCache; 031import org.opencms.file.CmsFile; 032import org.opencms.file.CmsObject; 033import org.opencms.file.CmsResource; 034import org.opencms.file.types.CmsResourceTypeFunctionConfig; 035import org.opencms.i18n.CmsLocaleManager; 036import org.opencms.main.CmsException; 037import org.opencms.util.CmsPair; 038import org.opencms.util.CmsStringUtil; 039import org.opencms.util.CmsUUID; 040import org.opencms.xml.containerpage.CmsDynamicFunctionBean.Format; 041import org.opencms.xml.content.CmsXmlContent; 042import org.opencms.xml.content.CmsXmlContentFactory; 043import org.opencms.xml.content.CmsXmlContentProperty; 044import org.opencms.xml.content.CmsXmlContentRootLocation; 045import org.opencms.xml.content.I_CmsXmlContentLocation; 046import org.opencms.xml.content.I_CmsXmlContentValueLocation; 047 048import java.util.ArrayList; 049import java.util.LinkedHashMap; 050import java.util.List; 051import java.util.Locale; 052import java.util.Map; 053 054/** 055 * The parser class for creating dynamic function beans from XML contents.<p> 056 */ 057public class CmsDynamicFunctionParser { 058 059 /** The node name for the formatter settings. */ 060 public static final String N_CONTAINER_SETTINGS = "ContainerSettings"; 061 062 /** 063 * Parses a dynamic function bean given a resource.<p> 064 * 065 * @param cms the current CMS context 066 * @param res the resource from which to read the dynamic function 067 * 068 * @return the dynamic function bean created from the resource 069 * 070 * @throws CmsException if something goes wrong 071 */ 072 public CmsDynamicFunctionBean parseFunctionBean(CmsObject cms, CmsResource res) throws CmsException { 073 074 CmsFile file = cms.readFile(res); 075 CmsXmlContent xmlContent = CmsXmlContentFactory.unmarshal(cms, file); 076 return parseFunctionBean(cms, xmlContent); 077 } 078 079 /** 080 * Parses a dynamic function bean from an in-memory XML content object.<p> 081 * 082 * @param cms the current CMS context 083 * @param content the XML content from which to read the dynamic function bean 084 * 085 * @return the dynamic function bean read from the XML content 086 * 087 * @throws CmsException if something goes wrong 088 */ 089 public CmsDynamicFunctionBean parseFunctionBean(CmsObject cms, CmsXmlContent content) throws CmsException { 090 091 Locale locale = getLocaleToUse(cms, content); 092 String oldSiteRoot = cms.getRequestContext().getSiteRoot(); 093 try { 094 cms.getRequestContext().setSiteRoot(""); 095 CmsResource functionFormatter = getFunctionFormatter(cms); 096 CmsXmlContentRootLocation root = new CmsXmlContentRootLocation(content, locale); 097 CmsDynamicFunctionBean functionBean = parseFunctionBean(cms, root, content.getFile(), functionFormatter); 098 return functionBean; 099 } finally { 100 cms.getRequestContext().setSiteRoot(oldSiteRoot); 101 } 102 } 103 104 /** 105 * Parses all the additional formats from the XML content.<p> 106 * 107 * @param cms the current CMS context 108 * @param location the location from which to parse the additional formats 109 * @param functionRes the dynamic function resource 110 * 111 * @return the list of parsed formats 112 */ 113 protected List<Format> getAdditionalFormats( 114 CmsObject cms, 115 I_CmsXmlContentLocation location, 116 CmsResource functionRes) { 117 118 List<I_CmsXmlContentValueLocation> locations = location.getSubValues("AdditionalFormat"); 119 List<Format> formats = new ArrayList<Format>(); 120 for (I_CmsXmlContentValueLocation formatLocation : locations) { 121 Format format = parseAdditionalFormat(cms, formatLocation, functionRes); 122 formats.add(format); 123 } 124 return formats; 125 126 } 127 128 /** 129 * Gets the function formatter resource, possibly from the cache.<p> 130 * 131 * @param cms the current CMS context 132 * @return the function formatter resource 133 * 134 * @throws CmsException if something goes wrong 135 */ 136 protected CmsResource getFunctionFormatter(CmsObject cms) throws CmsException { 137 138 CmsVfsMemoryObjectCache cache = CmsVfsMemoryObjectCache.getVfsMemoryObjectCache(); 139 String path = CmsResourceTypeFunctionConfig.FORMATTER_PATH; 140 Object cacheValue = cache.getCachedObject(cms, path); 141 if (cacheValue == null) { 142 CmsResource functionRes = cms.readResource(path); 143 cache.putCachedObject(cms, path, functionRes); 144 return functionRes; 145 } else { 146 return (CmsResource)cacheValue; 147 } 148 } 149 150 /** 151 * Gets the locale to use for parsing the dynamic function.<p> 152 * 153 * @param cms the current CMS context 154 * @param xmlContent the xml content from which the dynamic function should be read 155 * 156 * @return the locale from which the dynamic function should be read 157 */ 158 protected Locale getLocaleToUse(CmsObject cms, CmsXmlContent xmlContent) { 159 160 Locale contextLocale = cms.getRequestContext().getLocale(); 161 if (xmlContent.hasLocale(contextLocale)) { 162 return contextLocale; 163 } 164 Locale defaultLocale = CmsLocaleManager.getDefaultLocale(); 165 if (xmlContent.hasLocale(defaultLocale)) { 166 return defaultLocale; 167 } 168 if (!xmlContent.getLocales().isEmpty()) { 169 return xmlContent.getLocales().get(0); 170 } else { 171 return defaultLocale; 172 } 173 } 174 175 /** 176 * Parses the main format from the XML content.<p> 177 * @param cms the current CMS context 178 * @param location the location from which to parse main format 179 * @param functionRes the dynamic function resource 180 * 181 * @return the parsed main format 182 */ 183 protected Format getMainFormat(CmsObject cms, I_CmsXmlContentLocation location, CmsResource functionRes) { 184 185 I_CmsXmlContentValueLocation jspLoc = location.getSubValue("FunctionProvider"); 186 CmsUUID structureId = jspLoc.asId(cms); 187 I_CmsXmlContentValueLocation containerSettings = location.getSubValue("ContainerSettings"); 188 Map<String, String> parameters = parseParameters(cms, location, "Parameter"); 189 if (containerSettings != null) { 190 String type = getStringValue(cms, containerSettings.getSubValue("Type"), ""); 191 String minWidth = getStringValue(cms, containerSettings.getSubValue("MinWidth"), ""); 192 String maxWidth = getStringValue(cms, containerSettings.getSubValue("MaxWidth"), ""); 193 Format result = new Format(structureId, type, minWidth, maxWidth, parameters); 194 return result; 195 } else { 196 Format result = new Format(structureId, "", "", "", parameters); 197 result.setNoContainerSettings(true); 198 return result; 199 } 200 } 201 202 /** 203 * Gets the string value of an XML content location.<p> 204 * 205 * @param cms the current CMS context 206 * @param location an XML content location 207 * 208 * @return the string value of that XML content location 209 */ 210 protected String getString(CmsObject cms, I_CmsXmlContentValueLocation location) { 211 212 if (location == null) { 213 return null; 214 } 215 return location.asString(cms); 216 } 217 218 /** 219 * Converts a (possibly null) content value location to a string.<p> 220 * 221 * @param cms the current CMS context 222 * @param location the content value location 223 * @param defaultValue the value to return if the location is null 224 * 225 * @return the string value of the content value location 226 */ 227 protected String getStringValue(CmsObject cms, I_CmsXmlContentValueLocation location, String defaultValue) { 228 229 if (location == null) { 230 return defaultValue; 231 } 232 return location.asString(cms); 233 } 234 235 /** 236 * Parses an additional format from the XML content.<p> 237 * 238 * @param cms the current CMS context 239 * 240 * @param location the location from which to parse the additional format 241 * @param functionRes the dynamic function resource 242 * 243 * @return the additional format 244 */ 245 protected Format parseAdditionalFormat( 246 CmsObject cms, 247 I_CmsXmlContentValueLocation location, 248 CmsResource functionRes) { 249 250 I_CmsXmlContentValueLocation jspLoc = location.getSubValue("FunctionProvider"); 251 CmsUUID structureId = jspLoc.asId(cms); 252 I_CmsXmlContentValueLocation minWidthLoc = location.getSubValue("MinWidth"); 253 String minWidth = getStringValue(cms, minWidthLoc, ""); 254 I_CmsXmlContentValueLocation maxWidthLoc = location.getSubValue("MaxWidth"); 255 String maxWidth = getStringValue(cms, maxWidthLoc, ""); 256 I_CmsXmlContentValueLocation typeLoc = location.getSubValue("Type"); 257 String type = getStringValue(cms, typeLoc, ""); 258 Map<String, String> parameters = parseParameters(cms, location, "Parameter"); 259 return new Format(structureId, type, minWidth, maxWidth, parameters); 260 } 261 262 /** 263 * Parses a dynamic function bean.<p> 264 * 265 * @param cms the current CMS context 266 * @param location the location from which to parse the dynamic function bean 267 * @param functionRes the dynamic function resource 268 * @param functionFormatter the function formatter resource 269 * 270 * @return the parsed dynamic function bean 271 */ 272 protected CmsDynamicFunctionBean parseFunctionBean( 273 CmsObject cms, 274 I_CmsXmlContentLocation location, 275 CmsResource functionRes, 276 CmsResource functionFormatter) { 277 278 Format mainFormat = getMainFormat(cms, location, functionRes); 279 List<Format> otherFormats = getAdditionalFormats(cms, location, functionRes); 280 Map<String, CmsXmlContentProperty> definedSettings = parseSettings(cms, location, functionRes); 281 CmsDynamicFunctionBean result = new CmsDynamicFunctionBean( 282 mainFormat, 283 otherFormats, 284 definedSettings, 285 functionRes, 286 functionFormatter); 287 return result; 288 } 289 290 /** 291 * Parses a request parameter for the JSP from the XML content.<p> 292 * 293 * @param cms the current CMS context 294 * @param valueLocation the location from which to parse the parameter 295 * 296 * @return the parsed parameter key/value pair 297 */ 298 protected CmsPair<String, String> parseParameter(CmsObject cms, I_CmsXmlContentValueLocation valueLocation) { 299 300 String key = valueLocation.getSubValue("Key").asString(cms); 301 String value = valueLocation.getSubValue("Value").asString(cms); 302 return CmsPair.create(key, value); 303 } 304 305 /** 306 * Parses all parameters for the JSP from the XML content.<p> 307 * 308 * @param cms the current CMS context 309 * @param location the location from which to read the parameters 310 * @param name the name of the tag from which to read the parameters 311 * 312 * @return the parsed map of parameters 313 */ 314 protected Map<String, String> parseParameters(CmsObject cms, I_CmsXmlContentLocation location, String name) { 315 316 List<I_CmsXmlContentValueLocation> locations = location.getSubValues(name); 317 Map<String, String> result = new LinkedHashMap<String, String>(); 318 for (I_CmsXmlContentValueLocation paramLocation : locations) { 319 CmsPair<String, String> param = parseParameter(cms, paramLocation); 320 result.put(param.getFirst(), param.getSecond()); 321 } 322 return result; 323 } 324 325 /** 326 * Helper method for parsing a settings definition.<p> 327 * 328 * @param cms the current CMS context 329 * @param field the node from which to read the settings definition 330 * 331 * @return the parsed setting definition 332 */ 333 protected CmsXmlContentProperty parseProperty(CmsObject cms, I_CmsXmlContentLocation field) { 334 335 String name = getString(cms, field.getSubValue("PropertyName")); 336 String widget = getString(cms, field.getSubValue("Widget")); 337 if (CmsStringUtil.isEmptyOrWhitespaceOnly(widget)) { 338 widget = "string"; 339 } 340 String type = getString(cms, field.getSubValue("Type")); 341 if (CmsStringUtil.isEmptyOrWhitespaceOnly(type)) { 342 type = "string"; 343 } 344 String widgetConfig = getString(cms, field.getSubValue("WidgetConfig")); 345 String ruleRegex = getString(cms, field.getSubValue("RuleRegex")); 346 String ruleType = getString(cms, field.getSubValue("RuleType")); 347 String default1 = getString(cms, field.getSubValue("Default")); 348 String error = getString(cms, field.getSubValue("Error")); 349 String niceName = getString(cms, field.getSubValue("DisplayName")); 350 String description = getString(cms, field.getSubValue("Description")); 351 352 CmsXmlContentProperty prop = new CmsXmlContentProperty( 353 name, 354 type, 355 widget, 356 widgetConfig, 357 ruleRegex, 358 ruleType, 359 default1, 360 niceName, 361 description, 362 error, 363 "true"); 364 return prop; 365 } 366 367 /** 368 * Parses the settings for the dynamic function from the XML content.<p> 369 * 370 * @param cms the current CMS context 371 * @param location the location from which to read the dynamic function settings 372 * @param functionResource the dynamic function resource 373 * 374 * @return the parsed map of settings for the dynamic function 375 */ 376 protected Map<String, CmsXmlContentProperty> parseSettings( 377 CmsObject cms, 378 I_CmsXmlContentLocation location, 379 CmsResource functionResource) { 380 381 LinkedHashMap<String, CmsXmlContentProperty> settingConfigs = new LinkedHashMap<String, CmsXmlContentProperty>(); 382 List<I_CmsXmlContentValueLocation> locations = location.getSubValues("SettingConfig"); 383 for (I_CmsXmlContentValueLocation settingLoc : locations) { 384 CmsXmlContentProperty settingConfigBean = parseProperty(cms, settingLoc); 385 settingConfigs.put(settingConfigBean.getName(), settingConfigBean); 386 } 387 return settingConfigs; 388 } 389 390}