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