001/* 002 * Copyright 2018 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.runtime.model; 017 018import java.util.HashMap; 019import java.util.Map; 020import java.util.Map.Entry; 021 022import org.apache.avalon.framework.component.ComponentException; 023import org.apache.avalon.framework.configuration.Configuration; 024import org.apache.avalon.framework.configuration.ConfigurationException; 025import org.apache.avalon.framework.service.ServiceManager; 026import org.apache.commons.lang3.StringUtils; 027import org.slf4j.Logger; 028import org.slf4j.LoggerFactory; 029 030import org.ametys.plugins.core.ui.util.ConfigurationHelper; 031import org.ametys.runtime.config.DisableCondition; 032import org.ametys.runtime.config.DisableConditions; 033import org.ametys.runtime.i18n.I18nizableText; 034import org.ametys.runtime.model.type.ModelItemType; 035import org.ametys.runtime.parameter.DefaultValidator; 036import org.ametys.runtime.parameter.Validator; 037import org.ametys.runtime.plugin.component.AbstractThreadSafeComponentExtensionPoint; 038import org.ametys.runtime.plugin.component.ThreadSafeComponentManager; 039 040/** 041 * {@link ElementDefinition} parser from an XML configuration. 042 */ 043public class ElementDefinitionParser extends AbstractModelItemParser 044{ 045 private static final Logger __LOGGER = LoggerFactory.getLogger(ElementDefinitionParser.class); 046 047 private ThreadSafeComponentManager<Enumerator> _enumeratorManager; 048 private ThreadSafeComponentManager<Validator> _validatorManager; 049 private final Map<ElementDefinition, String> _validatorsToLookup = new HashMap<>(); 050 private final Map<ElementDefinition, String> _enumeratorsToLookup = new HashMap<>(); 051 052 /** 053 * Creates an element definition parser. 054 * @param modelItemTypeExtensionPoint the extension point to use to get available element types 055 * @param enumeratorManager the enumerator component manager. 056 * @param validatorManager the validator component manager. 057 */ 058 public ElementDefinitionParser(AbstractThreadSafeComponentExtensionPoint<? extends ModelItemType> modelItemTypeExtensionPoint, ThreadSafeComponentManager<Enumerator> enumeratorManager, ThreadSafeComponentManager<Validator> validatorManager) 059 { 060 super(modelItemTypeExtensionPoint); 061 _enumeratorManager = enumeratorManager; 062 _validatorManager = validatorManager; 063 } 064 065 @Override 066 @SuppressWarnings("unchecked") 067 public ElementDefinition parse(ServiceManager serviceManager, String pluginName, Configuration definitionConfig, Model model, ModelItemGroup parent) throws ConfigurationException 068 { 069 ElementDefinition definition = (ElementDefinition) super.parse(serviceManager, pluginName, definitionConfig, model, parent); 070 071 definition.setPluginName(pluginName); 072 definition.setDefaultValue(_parseDefaultValue(definitionConfig, definition)); 073 definition.setMultiple(_parseMultiple(definitionConfig)); 074 075 definition.setWidget(_parseWidget(definitionConfig)); 076 definition.setWidgetParameters(_parseWidgetParameters(definitionConfig, pluginName)); 077 078 _parseAndSetEnumerator(pluginName, definition, definitionConfig); 079 _parseAndSetValidator(pluginName, definition, definitionConfig); 080 081 definition.setDisableConditions(_parseDisableConditions(definitionConfig)); 082 083 return definition; 084 } 085 086 @Override 087 protected ElementDefinition _createModelItem(Configuration definitionConfig) throws ConfigurationException 088 { 089 return new ElementDefinition(); 090 } 091 092 /** 093 * Parses the default value. 094 * @param definitionConfig the element definition configuration. 095 * @param definition the element definition. 096 * @return the default value or <code>null</code> if none defined. 097 * @throws ConfigurationException if the configuration is not valid. 098 */ 099 protected Object _parseDefaultValue(Configuration definitionConfig, ElementDefinition definition) throws ConfigurationException 100 { 101 Configuration childNode = definitionConfig.getChild("default-value", false); 102 if (childNode != null) 103 { 104 return definition.getType().parseConfiguration(childNode); 105 } 106 return null; 107 } 108 109 /** 110 * Parses the multiple attribute. 111 * @param definitionConfig the element definition configuration to use. 112 * @return the true if the element is multiple, false otherwise. 113 * @throws ConfigurationException if the configuration is not valid. 114 */ 115 protected Boolean _parseMultiple(Configuration definitionConfig) throws ConfigurationException 116 { 117 return definitionConfig.getAttributeAsBoolean("multiple", false); 118 } 119 120 /** 121 * Parses the widget. 122 * @param definitionConfig the element definition configuration to use. 123 * @return the widget or <code>null</code> if none defined. 124 * @throws ConfigurationException if the configuration is not valid. 125 */ 126 protected String _parseWidget(Configuration definitionConfig) throws ConfigurationException 127 { 128 return definitionConfig.getChild("widget").getValue(null); 129 } 130 131 /** 132 * Parses the widget's parameters 133 * @param definitionConfig the parameter ele;ent definition to use. 134 * @param pluginName the current plugin name. 135 * @return the widget's parameters in a Map 136 * @throws ConfigurationException if the configuration is not valid. 137 */ 138 protected Map<String, I18nizableText> _parseWidgetParameters(Configuration definitionConfig, String pluginName) throws ConfigurationException 139 { 140 Map<String, I18nizableText> widgetParams = new HashMap<>(); 141 142 Configuration widgetParamsConfig = definitionConfig.getChild("widget-params", false); 143 if (widgetParamsConfig != null) 144 { 145 Map<String, Object> parsedParams = ConfigurationHelper.parsePluginParameters(widgetParamsConfig, pluginName, __LOGGER); 146 147 for (Entry<String, Object> param : parsedParams.entrySet()) 148 { 149 String paramName = param.getKey(); 150 Object value = param.getValue(); 151 if (value instanceof I18nizableText) 152 { 153 widgetParams.put(paramName, (I18nizableText) value); 154 } 155 else if (value instanceof String) 156 { 157 widgetParams.put(paramName, new I18nizableText((String) value)); 158 } 159 else 160 { 161 __LOGGER.warn("Widget parameter '{}' at location {} is of type [{}] which is not supported. It will be ignored.", paramName, definitionConfig.getLocation(), value.getClass()); 162 } 163 } 164 } 165 166 return widgetParams; 167 } 168 169 /** 170 * Parses the enumerator. 171 * @param pluginName the plugin name. 172 * @param definition the element definition. 173 * @param definitionConfig the element definition configuration. 174 * @throws ConfigurationException if the configuration is not valid. 175 */ 176 @SuppressWarnings("unchecked") 177 protected void _parseAndSetEnumerator(String pluginName, ElementDefinition definition, Configuration definitionConfig) throws ConfigurationException 178 { 179 Configuration enumeratorConfig = definitionConfig.getChild("enumeration", false); 180 181 if (enumeratorConfig != null) 182 { 183 Configuration customEnumerator = enumeratorConfig.getChild("custom-enumerator", false); 184 185 if (customEnumerator != null) 186 { 187 final String enumeratorClassName = customEnumerator.getAttribute("class"); 188 final String enumeratorRole = definition.getPath(); 189 190 try 191 { 192 Class enumeratorClass = Class.forName(enumeratorClassName); 193 _enumeratorManager.addComponent(pluginName, null, enumeratorRole, enumeratorClass, definitionConfig); 194 } 195 catch (Exception e) 196 { 197 throw new ConfigurationException("Unable to instantiate enumerator for class: " + enumeratorClassName, e); 198 } 199 200 // This enumerator will be affected later when enumeratorManager 201 // will be initialized in lookupComponents() call 202 _enumeratorsToLookup.put(definition, enumeratorRole); 203 204 } 205 else 206 { 207 StaticEnumerator staticEnumerator = new StaticEnumerator(); 208 209 for (Configuration entryConfig : enumeratorConfig.getChildren("entry")) 210 { 211 Configuration valueConfiguration = entryConfig.getChild("value"); 212 Object value = definition.getType().parseConfiguration(valueConfiguration); 213 I18nizableText label = null; 214 215 if (entryConfig.getChild("label", false) != null) 216 { 217 label = _parseI18nizableText(entryConfig, pluginName, "label"); 218 } 219 220 staticEnumerator.add(label, value); 221 } 222 223 definition.setEnumerator(staticEnumerator); 224 } 225 } 226 } 227 228 /** 229 * Parses the validator. 230 * @param pluginName the plugin name. 231 * @param definition the element definition. 232 * @param definitionConfig the element definition configuration. 233 * @throws ConfigurationException if the configuration is not valid. 234 */ 235 @SuppressWarnings("unchecked") 236 protected void _parseAndSetValidator(String pluginName, ElementDefinition definition, Configuration definitionConfig) throws ConfigurationException 237 { 238 Configuration validatorConfig = definitionConfig.getChild("validation", false); 239 240 if (validatorConfig != null) 241 { 242 final String validatorClassName = StringUtils.defaultIfBlank(validatorConfig.getChild("custom-validator").getAttribute("class", ""), DefaultValidator.class.getName()); 243 final String validatorRole = definition.getPath(); 244 245 try 246 { 247 Class validatorClass = Class.forName(validatorClassName); 248 _validatorManager.addComponent(pluginName, null, validatorRole, validatorClass, definitionConfig); 249 } 250 catch (Exception e) 251 { 252 throw new ConfigurationException("Unable to instantiate validator for class: " + validatorClassName, e); 253 } 254 255 // Will be affected later when validatorManager will be initialized 256 // in lookupComponents() call 257 _validatorsToLookup.put(definition, validatorRole); 258 } 259 } 260 261 /** 262 * Parses the disable condition. 263 * @param definitionConfiguration the configuration of the disable condition 264 * @return result the parsed disable condition to be converted in JSON 265 * @throws ConfigurationException if an error occurred 266 */ 267 protected DisableConditions _parseDisableConditions(Configuration definitionConfiguration) throws ConfigurationException 268 { 269 Configuration disableConditionConfiguration = definitionConfiguration.getChild("disable-conditions", false); 270 DisableConditions conditions = null; 271 272 if (disableConditionConfiguration != null) 273 { 274 conditions = new DisableConditions(); 275 276 Configuration[] conditionsConfiguration = disableConditionConfiguration.getChildren(); 277 for (Configuration conditionConfiguration : conditionsConfiguration) 278 { 279 String tagName = conditionConfiguration.getName(); 280 281 // Recursive case 282 if (tagName.equals("conditions")) 283 { 284 conditions.getSubConditions().add(_parseDisableConditions(conditionConfiguration)); 285 } 286 else if (tagName.equals("condition")) 287 { 288 String id = conditionConfiguration.getAttribute("id"); 289 DisableCondition.OPERATOR operator = DisableCondition.OPERATOR.valueOf(conditionConfiguration.getAttribute("operator", "eq").toUpperCase()); 290 String value = conditionConfiguration.getValue(""); 291 292 293 DisableCondition condition = new DisableCondition(id, operator, value); 294 conditions.getConditions().add(condition); 295 } 296 } 297 298 conditions.setAssociation(DisableConditions.ASSOCIATION_TYPE.valueOf(disableConditionConfiguration.getAttribute("type", "and").toUpperCase())); 299 } 300 301 return conditions; 302 } 303 304 /** 305 * Retrieves local validators and enumerators components and set them into 306 * previous parsed element definition. 307 * @throws Exception if an error occurs. 308 */ 309 @SuppressWarnings("unchecked") 310 public void lookupComponents() throws Exception 311 { 312 _validatorManager.initialize(); 313 _enumeratorManager.initialize(); 314 315 for (Map.Entry<ElementDefinition, String> entry : _validatorsToLookup.entrySet()) 316 { 317 ElementDefinition definition = entry.getKey(); 318 String validatorRole = entry.getValue(); 319 320 try 321 { 322 definition.setValidator(_validatorManager.lookup(validatorRole)); 323 } 324 catch (ComponentException e) 325 { 326 throw new Exception("Unable to lookup validator role: '" + validatorRole + "' for parameter: " + definition, e); 327 } 328 } 329 330 for (Map.Entry<ElementDefinition, String> entry : _enumeratorsToLookup.entrySet()) 331 { 332 ElementDefinition definition = entry.getKey(); 333 String enumeratorRole = entry.getValue(); 334 335 try 336 { 337 definition.setEnumerator(_enumeratorManager.lookup(enumeratorRole)); 338 } 339 catch (ComponentException e) 340 { 341 throw new Exception("Unable to lookup enumerator role: '" + enumeratorRole + "' for parameter: " + definition, e); 342 } 343 } 344 } 345}