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.List;
020import java.util.Map;
021import java.util.UUID;
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.Pair;
028
029import org.ametys.runtime.i18n.I18nizableText;
030import org.ametys.runtime.model.disableconditions.DisableConditions;
031import org.ametys.runtime.model.type.ModelItemTypeExtensionPoint;
032import org.ametys.runtime.parameter.DefaultValidator;
033import org.ametys.runtime.parameter.Validator;
034import org.ametys.runtime.plugin.component.ThreadSafeComponentManager;
035
036/**
037 * {@link ElementDefinition} parser from an XML configuration.
038 * @param <D> The type of disable conditions managed by the parsed item
039 */
040public abstract class AbstractElementDefinitionParser<D extends DisableConditions> extends AbstractModelItemParser<D>
041{
042    private ThreadSafeComponentManager<Enumerator> _enumeratorManager;
043    private ThreadSafeComponentManager<Validator> _validatorManager;
044    private final Map<String, ElementDefinition> _validatorsToLookup = new HashMap<>();
045    private final Map<String, ElementDefinition> _enumeratorsToLookup = new HashMap<>();
046    
047    /**
048     * Creates an element definition parser.
049     * @param modelItemTypeExtensionPoint the extension point to use to get available element types
050     * @param disableConditionsManager the disable conditions component manager
051     * @param enumeratorManager the enumerator component manager.
052     * @param validatorManager the validator component manager.
053     */
054    public AbstractElementDefinitionParser(ModelItemTypeExtensionPoint modelItemTypeExtensionPoint, ThreadSafeComponentManager<DisableConditions> disableConditionsManager, ThreadSafeComponentManager<Enumerator> enumeratorManager, ThreadSafeComponentManager<Validator> validatorManager)
055    {
056        super(modelItemTypeExtensionPoint, disableConditionsManager);
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(ItemParserHelper.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        return ItemParserHelper.parseDefaultValues(definitionConfig, definition, ItemParserHelper::parseDefaultValue);
092    }
093    
094    /**
095     * Parses the enumerator.
096     * @param pluginName the plugin name.
097     * @param catalog the catalog
098     * @param definition the element definition.
099     * @param definitionConfig the element definition configuration.
100     * @throws ConfigurationException if the configuration is not valid.
101     */
102    @SuppressWarnings("unchecked")
103    protected void _parseAndSetEnumerator(String pluginName, String catalog, ElementDefinition definition, Configuration definitionConfig) throws ConfigurationException
104    {
105        Configuration enumeratorConfig = definitionConfig.getChild("enumeration", false);
106        
107        if (enumeratorConfig != null)
108        {
109            Configuration customEnumerator = enumeratorConfig.getChild("custom-enumerator", false);
110            
111            if (customEnumerator != null)
112            {
113                final String enumeratorClassName = customEnumerator.getAttribute("class");
114                final String enumeratorRole = definition.getPath() + "$" + UUID.randomUUID().toString();
115                
116                try
117                {
118                    Class enumeratorClass = Class.forName(enumeratorClassName);
119                    _enumeratorManager.addComponent(pluginName, null, enumeratorRole, enumeratorClass, definitionConfig);
120                }
121                catch (Exception e)
122                {
123                    throw new ConfigurationException("Unable to instantiate enumerator for class: " + enumeratorClassName, e);
124                }
125
126                // This enumerator will be affected later when enumeratorManager
127                // will be initialized in lookupComponents() call
128                _enumeratorsToLookup.put(enumeratorRole, definition);
129                    
130                // Add the custom enumerator information to the element definition
131                definition.setCustomEnumerator(enumeratorClassName);
132                definition.setEnumeratorConfiguration(customEnumerator);
133            }
134            else
135            {
136                StaticEnumerator staticEnumerator = new StaticEnumerator();
137                
138                for (Configuration entryConfig : enumeratorConfig.getChildren("entry"))
139                {
140                    Configuration valueConfiguration = entryConfig.getChild("value");
141                    Object value = definition.getType().parseConfiguration(valueConfiguration);
142                    I18nizableText label = null;
143                    
144                    if (entryConfig.getChild("label", false) != null)
145                    {
146                        label = _parseI18nizableText(entryConfig, catalog, "label");
147                    }
148                    
149                    staticEnumerator.add(label, value);
150                }
151                
152                definition.setEnumerator(staticEnumerator);
153            }
154        }
155    }
156
157    /**
158     * Parses the validator.
159     * @param pluginName the plugin name.
160     * @param definition the element definition.
161     * @param definitionConfig the element definition configuration.
162     * @throws ConfigurationException if the configuration is not valid.
163     */
164    @SuppressWarnings("unchecked")
165    protected void _parseAndSetValidator(String pluginName, ElementDefinition definition, Configuration definitionConfig) throws ConfigurationException
166    {
167        Configuration validatorConfig = definitionConfig.getChild("validation", false);
168        
169        if (validatorConfig != null)
170        {
171            Configuration customValidator = validatorConfig.getChild("custom-validator", false);
172            String validatorClassName;
173
174            if (customValidator != null)
175            {
176                validatorClassName = customValidator.getAttribute("class");
177                
178                // Add the custom validator information to the element definition
179                definition.setCustomValidator(validatorClassName);
180                definition.setValidatorConfiguration(customValidator);
181            }
182            else
183            {
184                validatorClassName = DefaultValidator.class.getName();
185            }
186            
187            final String validatorRole = definition.getPath() + "$" + UUID.randomUUID().toString();
188            
189            try
190            {
191                Class validatorClass = Class.forName(validatorClassName);
192                _validatorManager.addComponent(pluginName, null, validatorRole, validatorClass, definitionConfig);
193            }
194            catch (Exception e)
195            {
196                throw new ConfigurationException("Unable to instantiate validator for class: " + validatorClassName, e);
197            }
198
199            // Will be affected later when validatorManager will be initialized
200            // in lookupComponents() call
201            _validatorsToLookup.put(validatorRole, definition);
202        }
203    }
204    
205    @Override
206    @SuppressWarnings("unchecked")
207    public void lookupComponents() throws Exception
208    {
209        super.lookupComponents();
210        
211        _validatorManager.initialize();
212        _enumeratorManager.initialize();
213        
214        for (Map.Entry<String, ElementDefinition> entry : _validatorsToLookup.entrySet())
215        {
216            String validatorRole = entry.getKey();
217            ElementDefinition definition = entry.getValue();
218            
219            try
220            {
221                definition.setValidator(_validatorManager.lookup(validatorRole));
222            }
223            catch (ComponentException e)
224            {
225                throw new Exception("Unable to lookup validator role: '" + validatorRole + "' for parameter: " + definition, e);
226            }
227        }
228        
229        for (Map.Entry<String, ElementDefinition> entry : _enumeratorsToLookup.entrySet())
230        {
231            String enumeratorRole = entry.getKey();
232            ElementDefinition definition = entry.getValue();
233            
234            try
235            {
236                definition.setEnumerator(_enumeratorManager.lookup(enumeratorRole));
237            }
238            catch (ComponentException e)
239            {
240                throw new Exception("Unable to lookup enumerator role: '" + enumeratorRole + "' for parameter: " + definition, e);
241            }
242        }
243    }
244}