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.ArrayList; 019import java.util.HashMap; 020import java.util.List; 021import java.util.Map; 022 023import org.apache.avalon.framework.component.ComponentException; 024import org.apache.avalon.framework.configuration.Configuration; 025import org.apache.avalon.framework.configuration.ConfigurationException; 026import org.apache.avalon.framework.service.ServiceManager; 027import org.apache.commons.lang3.tuple.ImmutablePair; 028import org.apache.commons.lang3.tuple.Pair; 029 030import org.ametys.runtime.config.ConfigManager; 031import org.ametys.runtime.i18n.I18nizableText; 032import org.ametys.runtime.model.type.ElementType; 033import org.ametys.runtime.model.type.ModelItemTypeExtensionPoint; 034import org.ametys.runtime.parameter.DefaultValidator; 035import org.ametys.runtime.parameter.Validator; 036import org.ametys.runtime.plugin.component.ThreadSafeComponentManager; 037 038/** 039 * {@link ElementDefinition} parser from an XML configuration. 040 */ 041public class ElementDefinitionParser extends AbstractModelItemParser 042{ 043 private ThreadSafeComponentManager<Enumerator> _enumeratorManager; 044 private ThreadSafeComponentManager<Validator> _validatorManager; 045 private final Map<ElementDefinition, String> _validatorsToLookup = new HashMap<>(); 046 private final Map<ElementDefinition, String> _enumeratorsToLookup = new HashMap<>(); 047 048 /** 049 * Creates an element definition parser. 050 * @param modelItemTypeExtensionPoint the extension point to use to get available element types 051 * @param enumeratorManager the enumerator component manager. 052 * @param validatorManager the validator component manager. 053 */ 054 public ElementDefinitionParser(ModelItemTypeExtensionPoint modelItemTypeExtensionPoint, ThreadSafeComponentManager<Enumerator> enumeratorManager, ThreadSafeComponentManager<Validator> validatorManager) 055 { 056 super(modelItemTypeExtensionPoint); 057 _enumeratorManager = enumeratorManager; 058 _validatorManager = validatorManager; 059 } 060 061 @Override 062 @SuppressWarnings("unchecked") 063 public <T extends ModelItem> T parse(ServiceManager serviceManager, String pluginName, String catalog, Configuration definitionConfig, Model model, ModelItemGroup parent) throws ConfigurationException 064 { 065 ElementDefinition definition = (ElementDefinition) super.parse(serviceManager, pluginName, catalog, definitionConfig, model, parent); 066 067 definition.setParsedDefaultValues(_parseDefaultValues(definitionConfig, definition)); 068 definition.setMultiple(_parseMultiple(definitionConfig)); 069 070 _parseAndSetEnumerator(pluginName, catalog, definition, definitionConfig); 071 _parseAndSetValidator(pluginName, definition, definitionConfig); 072 073 return (T) definition; 074 } 075 076 @Override 077 protected ElementDefinition _createModelItem(Configuration definitionConfig) throws ConfigurationException 078 { 079 return new DefaultElementDefinition(); 080 } 081 082 /** 083 * Parses the default values. 084 * @param definitionConfig the element definition configuration. 085 * @param definition the element definition. 086 * @return the default values and their types or <code>null</code> if none default value is defined. 087 * @throws ConfigurationException if the configuration is not valid. 088 */ 089 protected List<Pair<String, Object>> _parseDefaultValues(Configuration definitionConfig, ElementDefinition definition) throws ConfigurationException 090 { 091 Configuration[] defaultValueConfigs = definitionConfig.getChildren("default-value"); 092 if (defaultValueConfigs.length > 0) 093 { 094 List<Pair<String, Object>> defaultValues = new ArrayList<>(); 095 096 for (Configuration defaultValueConfig : defaultValueConfigs) 097 { 098 String defaultValueType = defaultValueConfig.getAttribute("type", null); 099 Object defaultValue = _parseDefaultValue(defaultValueConfig, definition, defaultValueType); 100 defaultValues.add(new ImmutablePair<>(defaultValueType, defaultValue)); 101 } 102 103 return defaultValues; 104 } 105 else 106 { 107 return null; 108 } 109 } 110 111 /** 112 * Parses the default value. 113 * @param defaultValueConfig the default value configuration. 114 * @param definition the element definition. 115 * @param defaultValueType the type of the default value 116 * @return the default value or <code>null</code> if none default value is defined. 117 * @throws ConfigurationException if the configuration is not valid. 118 */ 119 protected Object _parseDefaultValue(Configuration defaultValueConfig, ElementDefinition definition, String defaultValueType) throws ConfigurationException 120 { 121 ElementType type = definition.getType(); 122 if (defaultValueType != null) 123 { 124 if (ElementDefinition.CONFIG_DEFAULT_VALUE_TYPE.equals(defaultValueType)) 125 { 126 String configParamName = defaultValueConfig.getValue(); 127 if (ConfigManager.getInstance().hasModelItem(configParamName)) 128 { 129 ModelItem configParamDefinition = ConfigManager.getInstance().getModelItem(configParamName); 130 String configParamTypeId = configParamDefinition.getType().getId(); 131 if (configParamTypeId.equals(type.getId())) 132 { 133 return configParamName; 134 } 135 else 136 { 137 throw new ConfigurationException("The configuration parameter '" + configParamName + " (" + configParamTypeId + ")' cannot be used as default value for item '" + definition.getPath() + " (" + type.getId() + ")': types are not the same.", defaultValueConfig); 138 } 139 } 140 else 141 { 142 throw new ConfigurationException("The configuration parameter '" + configParamName + "' does not exist, it cannot be used as default value for item '" + definition.getPath() + " (" + type.getId() + ")'.", defaultValueConfig); 143 } 144 } 145 else 146 { 147 throw new ConfigurationException("The type '" + defaultValueType + "' is not available for the default value of item '" + definition.getPath() + " (" + type.getId() + ")'.", defaultValueConfig); 148 } 149 } 150 else 151 { 152 return type.parseConfiguration(defaultValueConfig); 153 } 154 } 155 156 /** 157 * Parses the multiple attribute. 158 * @param definitionConfig the element definition configuration to use. 159 * @return the true if the element is multiple, false otherwise. 160 * @throws ConfigurationException if the configuration is not valid. 161 */ 162 protected Boolean _parseMultiple(Configuration definitionConfig) throws ConfigurationException 163 { 164 return definitionConfig.getAttributeAsBoolean("multiple", false); 165 } 166 167 /** 168 * Parses the enumerator. 169 * @param pluginName the plugin name. 170 * @param catalog the catalog 171 * @param definition the element definition. 172 * @param definitionConfig the element definition configuration. 173 * @throws ConfigurationException if the configuration is not valid. 174 */ 175 @SuppressWarnings("unchecked") 176 protected void _parseAndSetEnumerator(String pluginName, String catalog, ElementDefinition definition, Configuration definitionConfig) throws ConfigurationException 177 { 178 Configuration enumeratorConfig = definitionConfig.getChild("enumeration", false); 179 180 if (enumeratorConfig != null) 181 { 182 Configuration customEnumerator = enumeratorConfig.getChild("custom-enumerator", false); 183 184 if (customEnumerator != null) 185 { 186 final String enumeratorClassName = customEnumerator.getAttribute("class"); 187 final String enumeratorRole = definition.getPath(); 188 189 try 190 { 191 Class enumeratorClass = Class.forName(enumeratorClassName); 192 _enumeratorManager.addComponent(pluginName, null, enumeratorRole, enumeratorClass, definitionConfig); 193 } 194 catch (Exception e) 195 { 196 throw new ConfigurationException("Unable to instantiate enumerator for class: " + enumeratorClassName, e); 197 } 198 199 // This enumerator will be affected later when enumeratorManager 200 // will be initialized in lookupComponents() call 201 _enumeratorsToLookup.put(definition, enumeratorRole); 202 203 // Add the custom enumerator information to the element definition 204 definition.setCustomEnumerator(enumeratorClassName); 205 definition.setEnumeratorConfiguration(customEnumerator); 206 } 207 else 208 { 209 StaticEnumerator staticEnumerator = new StaticEnumerator(); 210 211 for (Configuration entryConfig : enumeratorConfig.getChildren("entry")) 212 { 213 Configuration valueConfiguration = entryConfig.getChild("value"); 214 Object value = definition.getType().parseConfiguration(valueConfiguration); 215 I18nizableText label = null; 216 217 if (entryConfig.getChild("label", false) != null) 218 { 219 label = _parseI18nizableText(entryConfig, catalog, "label"); 220 } 221 222 staticEnumerator.add(label, value); 223 } 224 225 definition.setEnumerator(staticEnumerator); 226 } 227 } 228 } 229 230 /** 231 * Parses the validator. 232 * @param pluginName the plugin name. 233 * @param definition the element definition. 234 * @param definitionConfig the element definition configuration. 235 * @throws ConfigurationException if the configuration is not valid. 236 */ 237 @SuppressWarnings("unchecked") 238 protected void _parseAndSetValidator(String pluginName, ElementDefinition definition, Configuration definitionConfig) throws ConfigurationException 239 { 240 Configuration validatorConfig = definitionConfig.getChild("validation", false); 241 242 if (validatorConfig != null) 243 { 244 Configuration customValidator = validatorConfig.getChild("custom-validator", false); 245 String validatorClassName; 246 247 if (customValidator != null) 248 { 249 validatorClassName = customValidator.getAttribute("class"); 250 251 // Add the custom validator information to the element definition 252 definition.setCustomValidator(validatorClassName); 253 definition.setValidatorConfiguration(customValidator); 254 } 255 else 256 { 257 validatorClassName = DefaultValidator.class.getName(); 258 } 259 260 final String validatorRole = definition.getPath(); 261 262 try 263 { 264 Class validatorClass = Class.forName(validatorClassName); 265 _validatorManager.addComponent(pluginName, null, validatorRole, validatorClass, definitionConfig); 266 } 267 catch (Exception e) 268 { 269 throw new ConfigurationException("Unable to instantiate validator for class: " + validatorClassName, e); 270 } 271 272 // Will be affected later when validatorManager will be initialized 273 // in lookupComponents() call 274 _validatorsToLookup.put(definition, validatorRole); 275 } 276 } 277 278 /** 279 * Retrieves local validators and enumerators components and set them into 280 * previous parsed element definition. 281 * @throws Exception if an error occurs. 282 */ 283 @SuppressWarnings("unchecked") 284 public void lookupComponents() throws Exception 285 { 286 _validatorManager.initialize(); 287 _enumeratorManager.initialize(); 288 289 for (Map.Entry<ElementDefinition, String> entry : _validatorsToLookup.entrySet()) 290 { 291 ElementDefinition definition = entry.getKey(); 292 String validatorRole = entry.getValue(); 293 294 try 295 { 296 definition.setValidator(_validatorManager.lookup(validatorRole)); 297 } 298 catch (ComponentException e) 299 { 300 throw new Exception("Unable to lookup validator role: '" + validatorRole + "' for parameter: " + definition, e); 301 } 302 } 303 304 for (Map.Entry<ElementDefinition, String> entry : _enumeratorsToLookup.entrySet()) 305 { 306 ElementDefinition definition = entry.getKey(); 307 String enumeratorRole = entry.getValue(); 308 309 try 310 { 311 definition.setEnumerator(_enumeratorManager.lookup(enumeratorRole)); 312 } 313 catch (ComponentException e) 314 { 315 throw new Exception("Unable to lookup enumerator role: '" + enumeratorRole + "' for parameter: " + definition, e); 316 } 317 } 318 } 319}