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