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.UUID;
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;
027
028import org.ametys.runtime.i18n.I18nizableText;
029import org.ametys.runtime.model.disableconditions.DisableConditions;
030import org.ametys.runtime.model.exception.UnknownTypeException;
031import org.ametys.runtime.model.type.ModelItemType;
032import org.ametys.runtime.model.type.ModelItemTypeExtensionPoint;
033import org.ametys.runtime.plugin.component.ThreadSafeComponentManager;
034import org.ametys.runtime.util.ParameterizedTypesHelper;
035
036/**
037 * {@link ModelItem} parser from an XML configuration.
038 * @param <D> The type of disable conditions managed by the parsed item
039 */
040@SuppressWarnings("unchecked")
041public abstract class AbstractModelItemParser<D extends DisableConditions>
042{
043    /** The extension point to use to get available model items types */
044    protected ModelItemTypeExtensionPoint _modelItemTypeExtensionPoint;
045    
046    /** The disable conditions component manager */
047    protected ThreadSafeComponentManager<DisableConditions> _disableConditionsManager;
048    /** The disabled conditions to lookup */
049    protected final Map<String, ModelItem> _disableConditionsToLookup = new HashMap<>();
050    
051    private Class<D> _disableConditionsType;
052    {
053        _disableConditionsType = ParameterizedTypesHelper.getFirstActualClassArgument(AbstractModelItemParser.class, this.getClass());
054    }
055
056    /**
057     * Creates a model item parser.
058     * @param modelItemTypeExtensionPoint the extension point to use to get available model items types
059     * @param disableConditionsManager the disable conditions component manager
060     */
061    public AbstractModelItemParser(ModelItemTypeExtensionPoint modelItemTypeExtensionPoint, ThreadSafeComponentManager<DisableConditions> disableConditionsManager)
062    {
063        _modelItemTypeExtensionPoint = modelItemTypeExtensionPoint;
064        _disableConditionsManager = disableConditionsManager;
065    }
066    
067    /**
068     * Parses an element definition from a XML configuration.
069     * @param <T> type of the parsed item
070     * @param serviceManager the service manager
071     * @param pluginName the plugin name declaring this item.
072     * @param itemConfig the XML configuration of the model item.
073     * @param model the model which defines the model item
074     * @param parent the parent of the model item to create. Can be null if the model item to parse has no parent
075     * @return the parsed model item.
076     * @throws ConfigurationException if the configuration is not valid.
077     */
078    public <T extends ModelItem> T parse(ServiceManager serviceManager, String pluginName, Configuration itemConfig, Model model, ModelItemGroup parent) throws ConfigurationException
079    {
080        return (T) parse(serviceManager, pluginName, "plugin." + pluginName, itemConfig, model, parent);
081    }
082    
083    /**
084     * Parses an element definition from a XML configuration.
085     * @param <T> type of the parsed item
086     * @param serviceManager the service manager
087     * @param pluginName the plugin name declaring this item.
088     * @param catalog the catalog
089     * @param itemConfig the XML configuration of the model item.
090     * @param model the model which defines the model item
091     * @param parent the parent of the model item to create. Can be null if the model item to parse has no parent
092     * @return the parsed model item.
093     * @throws ConfigurationException if the configuration is not valid.
094     */
095    public <T extends ModelItem> T parse(ServiceManager serviceManager, String pluginName, String catalog, Configuration itemConfig, Model model, ModelItemGroup parent) throws ConfigurationException
096    {
097        ModelItem modelItem = _createModelItem(itemConfig);
098        
099        modelItem.setModel(model);
100        if (parent != null)
101        {
102            parent.addChild(modelItem);
103        }
104        
105        modelItem.setName(_parseName(itemConfig));
106        modelItem.setPluginName(pluginName);
107        modelItem.setLabel(_parseI18nizableText(itemConfig, catalog, "label"));
108        modelItem.setDescription(_parseI18nizableText(itemConfig, catalog, "description"));
109        modelItem.setType(_parseType(itemConfig));
110        
111        modelItem.setWidget(_parseWidget(itemConfig));
112        modelItem.setWidgetParameters(_parseWidgetParameters(itemConfig, pluginName));
113        
114        _parseDisableConditions(itemConfig, pluginName, modelItem);
115        
116        return (T) modelItem;
117    }
118    
119    /**
120     * Create the model item to populate it.
121     * @param itemConfig the model item configuration to use.
122     * @return the item instantiated.
123     * @throws ConfigurationException if the configuration is not valid.
124     */
125    protected abstract ModelItem _createModelItem(Configuration itemConfig) throws ConfigurationException;
126
127    /**
128     * Parses the name of the model item.
129     * @param itemConfig the model item configuration to use.
130     * @return the name of the model item.
131     * @throws ConfigurationException if the configuration is not valid.
132     */
133    protected String _parseName(Configuration itemConfig) throws ConfigurationException
134    {
135        return ItemParserHelper.parseName(itemConfig, _getNameConfigurationAttribute());
136    }
137    
138    /**
139     * Retrieves the name of the configuration attribute that contains the name of the model item
140     * @return the name of the configuration attribute that contains the name of the model item
141     */
142    protected String _getNameConfigurationAttribute()
143    {
144        return "name";
145    }
146    
147    /**
148     * Parses a mandatory i18n text configuration, throwing an exception if empty.
149     * @param config the configuration to use.
150     * @param catalog the catalog
151     * @param name the child name.
152     * @return the i18n text.
153     * @throws ConfigurationException if the configuration is not valid.
154     */
155    protected I18nizableText _parseI18nizableText(Configuration config, String catalog, String name) throws ConfigurationException
156    {
157        return I18nizableText.parseI18nizableText(config.getChild(name), catalog);
158    }
159    
160    /**
161     * Parse an optional i18n text configuration, with a default i18n text value.
162     * @param config the configuration to use.
163     * @param catalog the catalog
164     * @param name the child name.
165     * @param defaultValueCatalog the catalog of the default i18n text value
166     * @param defaultValue the default i18n text value.
167     * @return the i18n text.
168     */
169    protected I18nizableText _parseI18nizableText(Configuration config, String catalog, String name, String defaultValueCatalog, String defaultValue)
170    {
171        return I18nizableText.parseI18nizableText(config.getChild(name), catalog, new I18nizableText(defaultValueCatalog, defaultValue));
172    }
173    
174    /**
175     * Parses the type.
176     * @param config the model item configuration to use.
177     * @return the type.
178     * @throws ConfigurationException if the configuration is not valid.
179     */
180    protected ModelItemType _parseType(Configuration config) throws ConfigurationException
181    {
182        String typeId = config.getAttribute("type");
183        
184        if (!_modelItemTypeExtensionPoint.hasExtension(typeId))
185        {
186            String modelItemName = _parseName(config);
187            String availableTypes = StringUtils.join(_modelItemTypeExtensionPoint.getExtensionsIds(), ", ");
188            UnknownTypeException ute = new UnknownTypeException("The type '" + typeId + "' is not available for the extension point '" + _modelItemTypeExtensionPoint + "'. Available types are: '" + availableTypes + "'.");
189            throw new ConfigurationException("Unable to find the type '" + typeId + "' defined on the item '" + modelItemName + "'.", ute);
190        }
191        
192        return _modelItemTypeExtensionPoint.getExtension(typeId);
193    }
194    
195    /**
196     * Parses the disable conditions.
197     * @param pluginName the plugin name.
198     * @param modelItem the model item.
199     * @param definitionConfiguration the configuration of the model item
200     * @throws ConfigurationException if an error occurred
201     */
202    protected void _parseDisableConditions(Configuration definitionConfiguration, String pluginName, ModelItem modelItem) throws ConfigurationException
203    {
204        Configuration disableConditionsConfiguration = definitionConfiguration.getChild("disable-conditions", false);
205        if (disableConditionsConfiguration != null)
206        {
207            Class<? extends DisableConditions> conditionsClass = _disableConditionsType;
208            final String conditionsRole = conditionsClass + "$" + UUID.randomUUID().toString();
209            _disableConditionsManager.addComponent(pluginName, null, conditionsRole, conditionsClass, disableConditionsConfiguration);
210            _disableConditionsToLookup.put(conditionsRole, modelItem);
211        }
212    }
213    
214    /**
215     * Parses the widget.
216     * @param definitionConfig the element definition configuration to use.
217     * @return the widget or <code>null</code> if none defined.
218     * @throws ConfigurationException if the configuration is not valid.
219     */
220    protected String _parseWidget(Configuration definitionConfig) throws ConfigurationException
221    {
222        return ItemParserHelper.parseWidget(definitionConfig);
223    }
224    
225    /**
226     * Parses the widget's parameters
227     * @param definitionConfig the parameter ele;ent definition to use.
228     * @param pluginName the current plugin name.
229     * @return the widget's parameters in a Map
230     * @throws ConfigurationException if the configuration is not valid.
231     */
232    protected Map<String, I18nizableText> _parseWidgetParameters(Configuration definitionConfig, String pluginName) throws ConfigurationException
233    {
234        return ItemParserHelper.parseWidgetParameters(definitionConfig, pluginName);
235    }
236    
237    /**
238     * Retrieves components and set them into previous parsed model item.
239     * @throws Exception if an error occurs.
240     */
241    public void lookupComponents() throws Exception
242    {
243        _disableConditionsManager.initialize();
244        
245        for (Map.Entry<String, ModelItem> entry : _disableConditionsToLookup.entrySet())
246        {
247            String conditionsRole = entry.getKey();
248            ModelItem modelItem = entry.getValue();
249            
250            try
251            {
252                modelItem.setDisableConditions(_disableConditionsManager.lookup(conditionsRole));
253            }
254            catch (ComponentException e)
255            {
256                throw new Exception("Unable to lookup disable conditions role: '" + conditionsRole + "' for parameter: " + modelItem, e);
257            }
258        }
259    }
260}