001/* 002 * Copyright 2015 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.plugins.core.ui.util; 017 018import java.util.ArrayList; 019import java.util.HashMap; 020import java.util.LinkedHashMap; 021import java.util.List; 022import java.util.Map; 023 024import org.apache.avalon.framework.configuration.Configuration; 025import org.apache.avalon.framework.configuration.ConfigurationException; 026import org.apache.commons.lang3.StringUtils; 027import org.slf4j.Logger; 028 029import org.ametys.core.ui.ClientSideElement.ScriptFile; 030import org.ametys.runtime.config.Config; 031import org.ametys.runtime.i18n.I18nizableText; 032 033/** 034 * Helper class providing methods to deal with common {@link Configuration} tasks. 035 */ 036public final class ConfigurationHelper 037{ 038 039 private ConfigurationHelper() 040 { 041 // Helper class, never to be instantiated. 042 } 043 044 /** 045 * Parse a plugin resource list configuration. 046 * @param configuration The plugin resource list configuration. 047 * @param defaultPluginName The default plugin name to use for the resources. 048 * @param logger The logger. 049 * @return The list of complete URLs of files to import. 050 * @throws ConfigurationException If an error occurs 051 */ 052 public static List<ScriptFile> parsePluginResourceList(Configuration configuration, String defaultPluginName, Logger logger) throws ConfigurationException 053 { 054 List<ScriptFile> resourceFiles = new ArrayList<>(); 055 String listDefaultPlugin = configuration.getAttribute("plugin", defaultPluginName); 056 for (Configuration fileConfiguration : configuration.getChildren("file")) 057 { 058 ScriptFile scriptFile = _getPluginResourceValue(fileConfiguration, listDefaultPlugin, logger); 059 060 resourceFiles.add(scriptFile); 061 } 062 return resourceFiles; 063 } 064 065 private static ScriptFile _getPluginResourceValue(Configuration fileConfiguration, String listDefaultPlugin, Logger logger) throws ConfigurationException 066 { 067 ScriptFile scriptFile; 068 069 boolean langAware = fileConfiguration.getAttributeAsBoolean("lang", false); 070 if (langAware) 071 { 072 String defaultLang = fileConfiguration.getAttribute("defaultLang", null); 073 074 Map<String, String> langPaths = new HashMap<>(); 075 for (Configuration langConfiguration : fileConfiguration.getChildren("lang")) 076 { 077 String code = langConfiguration.getAttribute("code", null); 078 if (StringUtils.isBlank(code)) 079 { 080 throw new ConfigurationException("Code attribute is mandatory for lang tag", langConfiguration); 081 } 082 083 String path = _getPluginResourceValue(langConfiguration, listDefaultPlugin, langConfiguration.getValue(), logger); 084 langPaths.put(code, path); 085 } 086 087 scriptFile = new ScriptFile(langPaths, defaultLang); 088 } 089 else 090 { 091 String rtl = fileConfiguration.getAttribute("rtl", "all"); 092 String path = _getPluginResourceValue(fileConfiguration, listDefaultPlugin, fileConfiguration.getValue(), logger); 093 094 scriptFile = new ScriptFile(rtl, path); 095 } 096 097 return scriptFile; 098 } 099 100 /** 101 * Parse a mandatory plugin resource configuration. 102 * @param configuration The plugin resource configuration. 103 * @param defaultPluginName The default plugin name to use for the resources. 104 * @param logger The logger. 105 * @return The plugin resource full URL. 106 * @throws ConfigurationException If an error occurs 107 */ 108 public static String parsePluginResource(Configuration configuration, String defaultPluginName, Logger logger) throws ConfigurationException 109 { 110 String url = configuration.getValue(); 111 return _getPluginResourceValue(configuration, defaultPluginName, url, logger); 112 } 113 114 /** 115 * Parse an optional plugin resource configuration. 116 * @param configuration The plugin resource configuration. 117 * @param defaultPluginName The default plugin name to use for the resources. 118 * @param defaultValue The default value 119 * @param logger The logger. 120 * @return The plugin resource full URL. 121 */ 122 public static String parsePluginResource(Configuration configuration, String defaultPluginName, String defaultValue, Logger logger) 123 { 124 String url = configuration.getValue(defaultValue); 125 return _getPluginResourceValue(configuration, defaultPluginName, url, logger); 126 } 127 128 /** 129 * Get a plugin resource configuration value. 130 * @param configuration The plugin resource configuration. 131 * @param defaultPluginName The default plugin name to use for the resources. 132 * @param value The value to parse. 133 * @param logger The logger. 134 * @return The plugin resource full URL. 135 */ 136 private static String _getPluginResourceValue(Configuration configuration, String defaultPluginName, String value, Logger logger) 137 { 138 String pluginName = configuration.getAttribute("plugin", defaultPluginName); 139 140 String fullUrl = "/plugins/" + pluginName + "/resources/" + value; 141 142 if (logger.isDebugEnabled()) 143 { 144 logger.debug("Importing file '" + fullUrl + "'"); 145 } 146 147 return fullUrl; 148 } 149 150 /** 151 * Parse a plugin files list configuration and return the list of URIs. 152 * @param configuration The plugin files list configuration. 153 * @param defaultPluginName The default plugin name to use for the files. 154 * @param logger The logger. 155 * @return The list of complete URIs of files to import. 156 * @throws ConfigurationException If an error occurs 157 */ 158 public static List<String> parsePluginResourceUri(Configuration configuration, String defaultPluginName, Logger logger) throws ConfigurationException 159 { 160 List<String> fileURIs = new ArrayList<>(); 161 String listDefaultPlugin = configuration.getAttribute("plugin", defaultPluginName); 162 for (Configuration fileConfiguration : configuration.getChildren("file")) 163 { 164 String fileURI = _getPluginResourceUri(fileConfiguration, listDefaultPlugin, logger); 165 166 fileURIs.add(fileURI); 167 } 168 return fileURIs; 169 } 170 171 /** 172 * Get a plugin resource configuration value. 173 * @param configuration The plugin resource configuration. 174 * @param defaultPluginName The default plugin name to use for the resources. 175 * @param logger The logger. 176 * @return The plugin resource full URL. 177 * @throws ConfigurationException If an error occurs 178 */ 179 private static String _getPluginResourceUri(Configuration configuration, String defaultPluginName, Logger logger) throws ConfigurationException 180 { 181 String pluginName = configuration.getAttribute("plugin", defaultPluginName); 182 183 String value = configuration.getValue(); 184 185 String fullUrl = "plugin:" + pluginName + "://" + value; 186 187 if (logger.isDebugEnabled()) 188 { 189 logger.debug("Importing file uri '" + fullUrl + "'"); 190 } 191 192 return fullUrl; 193 } 194 195 /** 196 * Parse a mandatory configuration parameter {@link Configuration}. 197 * @param configuration The {@link Configuration} to parse. 198 * @return the configuration parameter value. 199 * @throws ConfigurationException if an error occurs. 200 */ 201 public static String parseConfigParameter(Configuration configuration) throws ConfigurationException 202 { 203 return _getConfigParameterValue(configuration.getValue()); 204 } 205 206 /** 207 * Parse an optional configuration parameter {@link Configuration}. 208 * @param configuration The {@link Configuration} to parse. 209 * @param defaultValue The default value. 210 * @return the configuration parameter value. 211 */ 212 public static String parseConfigParameter(Configuration configuration, String defaultValue) 213 { 214 return _getConfigParameterValue(configuration.getValue(defaultValue)); 215 } 216 217 /** 218 * Get a configuration parameter value. 219 * @param value The key to get in configuration 220 * @return the configuration parameter value. 221 */ 222 private static String _getConfigParameterValue(String value) 223 { 224 return Config.getInstance().getValue(value); 225 } 226 227 /** 228 * Parse a structured Configuration to an Object. 229 * The returned Object can be either a String or a Map<String, Object>. 230 * The map's values are either a String, a Map, or a List<String>. 231 * @param configuration The structured Configuration to parse. 232 * @return The parsed Object. 233 */ 234 public static Object parseObject(Configuration configuration) 235 { 236 return parseObject(configuration, null); 237 } 238 239 /** 240 * Parse a structured Configuration to an Object. 241 * The returned Object can be either a String or a Map<String, Object>. 242 * The map's values are either a String, a Map, or a List<String>. 243 * @param configuration The structured Configuration to parse. 244 * @param defaultValue The value to use when an empty tag is found (at any level in the tree). 245 * @return The parsed Object. 246 */ 247 @SuppressWarnings("unchecked") 248 public static Object parseObject(Configuration configuration, Object defaultValue) 249 { 250 String value = configuration.getValue(null); 251 Configuration[] children = configuration.getChildren(); 252 253 if (value != null) 254 { 255 // Mixed content cannot be found in a Configuration: if it has a value, 256 // it won't have any child. 257 return value; 258 } 259 else if (children.length > 0) 260 { 261 Map<String, Object> result = new LinkedHashMap<>(); 262 for (Configuration subConf : children) 263 { 264 String name = subConf.getName(); 265 Object subValue = parseObject(subConf, defaultValue); 266 boolean multiple = configuration.getChildren(name).length > 1; 267 268 if (multiple) 269 { 270 // More than one value with the same name: make it a list and append the current value. 271 List<Object> values = null; 272 if (result.containsKey(name)) 273 { 274 values = (List<Object>) result.get(name); 275 } 276 else 277 { 278 values = new ArrayList<>(); 279 result.put(name, values); 280 } 281 values.add(subValue); 282 } 283 else 284 { 285 // Only one value with this name: add it as a single value. 286 result.put(name, subValue); 287 } 288 } 289 return result; 290 } 291 else 292 { 293 return defaultValue; 294 } 295 } 296 297 /** 298 * Parse parameters recursively. 299 * @param configuration the parameters configuration. 300 * @param defaultPluginName The default plugin name. 301 * @param logger The logger. 302 * @return parameters in a Map 303 * @throws ConfigurationException If the configuration is incorrect. 304 */ 305 public static Map<String, Object> parsePluginParameters(Configuration configuration, String defaultPluginName, Logger logger) throws ConfigurationException 306 { 307 Map<String, Object> parameters = new LinkedHashMap<>(); 308 309 for (Configuration paramConfiguration : configuration.getChildren()) 310 { 311 String name; 312 if (paramConfiguration.getName().equals("param")) 313 { 314 name = paramConfiguration.getAttribute("name"); 315 } 316 else 317 { 318 name = paramConfiguration.getName(); 319 } 320 String value = paramConfiguration.getValue(""); 321 322 if (logger.isDebugEnabled()) 323 { 324 logger.debug("Configured with parameter '" + name + "' : '" + value + "'"); 325 } 326 327 if (isI18n(paramConfiguration)) 328 { 329 addParameter(parameters, name, I18nizableText.getI18nizableTextValue(paramConfiguration, "plugin." + defaultPluginName, value)); 330 } 331 else if (isResourceFile(paramConfiguration)) 332 { 333 addParameter(parameters, name, _getPluginResourceValue(paramConfiguration, defaultPluginName, value, logger)); 334 } 335 else if (isConfigParameter(paramConfiguration)) 336 { 337 addParameter(parameters, name, _getConfigParameterValue(value)); 338 } 339 else if (paramConfiguration.getChildren().length != 0) 340 { 341 addParameter(parameters, name, parsePluginParameters(paramConfiguration, defaultPluginName, logger)); 342 } 343 else 344 { 345 addParameter(parameters, name, value); 346 } 347 } 348 349 return parameters; 350 } 351 352 /** 353 * Clone existing parameters 354 * @param parameters The existing parameters 355 * @return The cloned 356 */ 357 public static Map<String, Object> clonePluginParameters(Map<String, Object> parameters) 358 { 359 Map<String, Object> clonedParameters = new LinkedHashMap<>(); 360 361 for (String name: parameters.keySet()) 362 { 363 Object value = parameters.get(name); 364 365 clonedParameters.put(name, _clonePluginParameter(value)); 366 } 367 368 return clonedParameters; 369 } 370 371 @SuppressWarnings("unchecked") 372 private static Object _clonePluginParameter(Object value) 373 { 374 if (value instanceof I18nizableText 375 || value instanceof String 376 // after this point, types cannot be created by static configuration, but are "JSONUtils compatible" and may be used by dynamic ClientSideElements 377 || value instanceof Boolean 378 || value instanceof Integer 379 || value instanceof Long 380 || value instanceof Double) 381 { 382 return value; 383 } 384 else if (value instanceof LinkedHashMap) 385 { 386 return clonePluginParameters((Map<String, Object>) value); 387 } 388 else if (value instanceof ArrayList) 389 { 390 List list = new ArrayList(); 391 for (Object v : (List) value) 392 { 393 list.add(_clonePluginParameter(v)); 394 } 395 return list; 396 } 397 else 398 { 399 throw new IllegalArgumentException("Cannot clone a parameter of type '" + value.getClass().getName() + "'"); 400 } 401 } 402 403 @SuppressWarnings("unchecked") 404 private static void addParameter(Map<String, Object> parameters, String name, Object newValue) 405 { 406 if (parameters.containsKey(name)) 407 { 408 Object values = parameters.get(name); 409 if (values instanceof List) 410 { 411 ((List<Object>) values).add(newValue); 412 } 413 else 414 { 415 List list = new ArrayList<>(); 416 list.add(values); 417 list.add(newValue); 418 parameters.put(name, list); 419 } 420 } 421 else 422 { 423 parameters.put(name, newValue); 424 } 425 } 426 427 private static boolean isResourceFile(Configuration config) 428 { 429 return config.getAttributeAsBoolean("file", false) || config.getAttribute("type", "").equals("file"); 430 } 431 432 private static boolean isConfigParameter(Configuration config) 433 { 434 return config.getAttributeAsBoolean("config", false) || config.getAttribute("type", "").equals("config"); 435 } 436 437 private static boolean isI18n(Configuration config) 438 { 439 return config.getAttributeAsBoolean("i18n", false) || config.getAttribute("type", "").equals("i18n"); 440 } 441}