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; 022import java.util.UUID; 023 024import org.apache.avalon.framework.component.ComponentException; 025import org.apache.avalon.framework.configuration.Configuration; 026import org.apache.avalon.framework.configuration.ConfigurationException; 027import org.apache.avalon.framework.service.ServiceManager; 028import org.apache.commons.lang3.tuple.ImmutablePair; 029import org.apache.commons.lang3.tuple.Pair; 030 031import org.ametys.runtime.config.ConfigManager; 032import org.ametys.runtime.i18n.I18nizableText; 033import org.ametys.runtime.model.type.ElementType; 034import org.ametys.runtime.model.type.ModelItemTypeExtensionPoint; 035import org.ametys.runtime.parameter.DefaultValidator; 036import org.ametys.runtime.parameter.Validator; 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 ThreadSafeComponentManager<Enumerator> _enumeratorManager; 045 private ThreadSafeComponentManager<Validator> _validatorManager; 046 private final Map<String, ElementDefinition> _validatorsToLookup = new HashMap<>(); 047 private final Map<String, ElementDefinition> _enumeratorsToLookup = new HashMap<>(); 048 049 /** 050 * Creates an element definition parser. 051 * @param modelItemTypeExtensionPoint the extension point to use to get available element types 052 * @param enumeratorManager the enumerator component manager. 053 * @param validatorManager the validator component manager. 054 */ 055 public ElementDefinitionParser(ModelItemTypeExtensionPoint modelItemTypeExtensionPoint, ThreadSafeComponentManager<Enumerator> enumeratorManager, ThreadSafeComponentManager<Validator> validatorManager) 056 { 057 super(modelItemTypeExtensionPoint); 058 _enumeratorManager = enumeratorManager; 059 _validatorManager = validatorManager; 060 } 061 062 @Override 063 @SuppressWarnings("unchecked") 064 public <T extends ModelItem> T parse(ServiceManager serviceManager, String pluginName, String catalog, Configuration definitionConfig, Model model, ModelItemGroup parent) throws ConfigurationException 065 { 066 ElementDefinition definition = (ElementDefinition) super.parse(serviceManager, pluginName, catalog, definitionConfig, model, parent); 067 068 definition.setParsedDefaultValues(_parseDefaultValues(definitionConfig, definition)); 069 definition.setMultiple(_parseMultiple(definitionConfig)); 070 071 _parseAndSetEnumerator(pluginName, catalog, definition, definitionConfig); 072 _parseAndSetValidator(pluginName, definition, definitionConfig); 073 074 return (T) definition; 075 } 076 077 @Override 078 protected ElementDefinition _createModelItem(Configuration definitionConfig) throws ConfigurationException 079 { 080 return new DefaultElementDefinition(); 081 } 082 083 /** 084 * Parses the default values. 085 * @param definitionConfig the element definition configuration. 086 * @param definition the element definition. 087 * @return the default values and their types or <code>null</code> if none default value is defined. 088 * @throws ConfigurationException if the configuration is not valid. 089 */ 090 protected List<Pair<String, Object>> _parseDefaultValues(Configuration definitionConfig, ElementDefinition definition) throws ConfigurationException 091 { 092 Configuration[] defaultValueConfigs = definitionConfig.getChildren("default-value"); 093 if (defaultValueConfigs.length > 0) 094 { 095 List<Pair<String, Object>> defaultValues = new ArrayList<>(); 096 097 for (Configuration defaultValueConfig : defaultValueConfigs) 098 { 099 String defaultValueType = defaultValueConfig.getAttribute("type", null); 100 Object defaultValue = _parseDefaultValue(defaultValueConfig, definition, defaultValueType); 101 defaultValues.add(new ImmutablePair<>(defaultValueType, defaultValue)); 102 } 103 104 return defaultValues; 105 } 106 else 107 { 108 return null; 109 } 110 } 111 112 /** 113 * Parses the default value. 114 * @param defaultValueConfig the default value configuration. 115 * @param definition the element definition. 116 * @param defaultValueType the type of the default value 117 * @return the default value or <code>null</code> if none default value is defined. 118 * @throws ConfigurationException if the configuration is not valid. 119 */ 120 protected Object _parseDefaultValue(Configuration defaultValueConfig, ElementDefinition definition, String defaultValueType) throws ConfigurationException 121 { 122 ElementType type = definition.getType(); 123 if (defaultValueType != null) 124 { 125 if (ElementDefinition.CONFIG_DEFAULT_VALUE_TYPE.equals(defaultValueType)) 126 { 127 String configParamName = defaultValueConfig.getValue(); 128 if (ConfigManager.getInstance().hasModelItem(configParamName)) 129 { 130 ModelItem configParamDefinition = ConfigManager.getInstance().getModelItem(configParamName); 131 String configParamTypeId = configParamDefinition.getType().getId(); 132 if (configParamTypeId.equals(type.getId())) 133 { 134 return configParamName; 135 } 136 else 137 { 138 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); 139 } 140 } 141 else 142 { 143 throw new ConfigurationException("The configuration parameter '" + configParamName + "' does not exist, it cannot be used as default value for item '" + definition.getPath() + " (" + type.getId() + ")'.", defaultValueConfig); 144 } 145 } 146 else 147 { 148 throw new ConfigurationException("The type '" + defaultValueType + "' is not available for the default value of item '" + definition.getPath() + " (" + type.getId() + ")'.", defaultValueConfig); 149 } 150 } 151 else 152 { 153 return type.parseConfiguration(defaultValueConfig); 154 } 155 } 156 157 /** 158 * Parses the multiple attribute. 159 * @param definitionConfig the element definition configuration to use. 160 * @return the true if the element is multiple, false otherwise. 161 * @throws ConfigurationException if the configuration is not valid. 162 */ 163 protected Boolean _parseMultiple(Configuration definitionConfig) throws ConfigurationException 164 { 165 return definitionConfig.getAttributeAsBoolean("multiple", false); 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() + "$" + UUID.randomUUID().toString(); 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(enumeratorRole, definition); 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() + "$" + UUID.randomUUID().toString(); 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(validatorRole, definition); 276 } 277 } 278 279 /** 280 * Retrieves local validators and enumerators components and set them into 281 * previous parsed element definition. 282 * @throws Exception if an error occurs. 283 */ 284 @SuppressWarnings("unchecked") 285 public void lookupComponents() throws Exception 286 { 287 _validatorManager.initialize(); 288 _enumeratorManager.initialize(); 289 290 for (Map.Entry<String, ElementDefinition> entry : _validatorsToLookup.entrySet()) 291 { 292 String validatorRole = entry.getKey(); 293 ElementDefinition definition = entry.getValue(); 294 295 try 296 { 297 definition.setValidator(_validatorManager.lookup(validatorRole)); 298 } 299 catch (ComponentException e) 300 { 301 throw new Exception("Unable to lookup validator role: '" + validatorRole + "' for parameter: " + definition, e); 302 } 303 } 304 305 for (Map.Entry<String, ElementDefinition> entry : _enumeratorsToLookup.entrySet()) 306 { 307 String enumeratorRole = entry.getKey(); 308 ElementDefinition definition = entry.getValue(); 309 310 try 311 { 312 definition.setEnumerator(_enumeratorManager.lookup(enumeratorRole)); 313 } 314 catch (ComponentException e) 315 { 316 throw new Exception("Unable to lookup enumerator role: '" + enumeratorRole + "' for parameter: " + definition, e); 317 } 318 } 319 } 320}