001/*
002 *  Copyright 2020 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.web.parameters;
017
018import java.util.LinkedHashMap;
019import java.util.Map;
020
021import org.apache.avalon.framework.component.Component;
022import org.apache.avalon.framework.configuration.Configuration;
023import org.apache.avalon.framework.configuration.ConfigurationException;
024import org.apache.avalon.framework.service.ServiceException;
025import org.apache.avalon.framework.service.ServiceManager;
026import org.apache.avalon.framework.service.Serviceable;
027import org.apache.commons.lang.StringUtils;
028
029import org.ametys.cms.data.holder.group.DataHolderRepeaterDefinitionParser;
030import org.ametys.cms.model.AbstractElementDefinitionParser;
031import org.ametys.plugins.repository.model.RepeaterDefinition;
032import org.ametys.runtime.i18n.I18nizableText;
033import org.ametys.runtime.model.ElementDefinition;
034import org.ametys.runtime.model.Model;
035import org.ametys.runtime.model.ModelItem;
036import org.ametys.runtime.model.ModelItemGroup;
037import org.ametys.runtime.model.ModelViewItem;
038import org.ametys.runtime.model.ModelViewItemGroup;
039import org.ametys.runtime.model.SimpleViewItemGroup;
040import org.ametys.runtime.model.View;
041import org.ametys.runtime.model.ViewElement;
042import org.ametys.runtime.model.ViewItem;
043import org.ametys.runtime.model.ViewItemContainer;
044import org.ametys.runtime.model.ViewItemGroup;
045import org.ametys.runtime.model.disableconditions.DisableConditions;
046import org.ametys.runtime.plugin.component.AbstractLogEnabled;
047
048/**
049 * Helper to parse parameters from configuration
050 */
051public class ViewAndParametersParser extends AbstractLogEnabled implements Component, Serviceable               
052{
053    /** Avalon Role */
054    public static final String ROLE = ViewAndParametersParser.class.getName();
055    
056    /** The service manager */
057    protected ServiceManager _manager;
058    
059    @Override
060    public void service(ServiceManager smanager) throws ServiceException
061    {
062        _manager = smanager;
063    }
064    
065    /**
066     * Parse the configuration to get parameters.
067     * @param parametersConfiguration the parameters configuration.
068     * @param pluginName the plugin name
069     * @param catalog the catalog
070     * @param model the model
071     * @param elementDefinitionParser the element definition parser
072     * @param repeaterDefinitionParser the repeater definition parser
073     * @return the parameters
074     * @throws ConfigurationException if an error occurs.
075     */
076    public ViewAndParameters parseParameters(Configuration parametersConfiguration, String pluginName, String catalog, Model model, AbstractElementDefinitionParser<? extends DisableConditions> elementDefinitionParser, DataHolderRepeaterDefinitionParser repeaterDefinitionParser) throws ConfigurationException
077    {
078        View view = new View();
079        Map<String, ModelItem> modelItems = new LinkedHashMap<>();
080        
081        view.setName(parametersConfiguration.getAttribute("name", null));
082        view.setLabel(_parseI18nizableText(parametersConfiguration, "label", pluginName, catalog));
083        view.setDescription(_parseI18nizableText(parametersConfiguration, "description", pluginName, catalog));
084        
085        // Parse all groups, fielsets and parameters to build the view
086        Configuration[] groupConfigurations = parametersConfiguration.getChildren("group");
087        if (groupConfigurations.length > 0)
088        {
089            if (parametersConfiguration.getChildren("fieldset").length > 0
090                    || parametersConfiguration.getChildren("parameter").length > 0
091                    || parametersConfiguration.getChildren("repeater").length > 0)
092            {
093                throw new ConfigurationException("This parameters configuration should not have parameters out of its groups.", parametersConfiguration);
094            }
095            
096            // Has groups.
097            for (Configuration groupConfiguration : groupConfigurations)
098            {
099                SimpleViewItemGroup group = _parseSimpleViewItemGroup(groupConfiguration, null, pluginName, catalog, view, modelItems, model, elementDefinitionParser, repeaterDefinitionParser);
100                group.setRole(ViewItemGroup.TAB_ROLE);
101                view.addViewItem(group);
102            }
103        }
104        else
105        {
106            _parseChildren(parametersConfiguration, view, null, pluginName, catalog, view, modelItems, model, elementDefinitionParser, repeaterDefinitionParser);
107        }
108        
109        return new ViewAndParameters(view, modelItems);
110    }
111    
112    /**
113     * Parses a simple view item group (group or fieldset)
114     * @param groupConfiguration the item group configuration
115     * @param modelParent The last model item group to use as parent for next definitions
116     * @param pluginName the plugin name
117     * @param catalog the catalog
118     * @param view the view
119     * @param modelItems the map of model items
120     * @param model the model
121     * @param elementDefinitionParser the element definition parser
122     * @param repeaterDefinitionParser the repeater definition parser
123     * @return the parsed simple view item group
124     * @throws ConfigurationException if an error occurs
125     */
126    protected SimpleViewItemGroup _parseSimpleViewItemGroup(Configuration groupConfiguration, ModelItemGroup modelParent, String pluginName, String catalog, View view, Map<String, ModelItem> modelItems, Model model, AbstractElementDefinitionParser<? extends DisableConditions> elementDefinitionParser, DataHolderRepeaterDefinitionParser repeaterDefinitionParser) throws ConfigurationException
127    {
128        SimpleViewItemGroup group = new SimpleViewItemGroup();
129        group.setName(groupConfiguration.getAttribute("name", null));
130        group.setLabel(_parseI18nizableText(groupConfiguration, "label", pluginName, catalog));
131        group.setDescription(_parseI18nizableText(groupConfiguration, "description", pluginName, catalog));
132        
133        _parseChildren(groupConfiguration, group, modelParent, pluginName, catalog, view, modelItems, model, elementDefinitionParser, repeaterDefinitionParser);
134        
135        return group;
136    }
137
138    /**
139     * Parses a service parameter
140     * @param paramConfiguration the parameter configuration
141     * @param parent the parent of the service parameter. Can be <code>null</code> if the parameter has no parent
142     * @param pluginName the plugin name
143     * @param catalog the catalog
144     * @param view the view
145     * @param modelItems the map of model item
146     * @param model the model
147     * @param elementDefinitionParser the element definition parser
148     * @param repeaterDefinitionParser the repeater definition parser
149     * @return the parsed parameter
150     * @throws ConfigurationException if an error occurs
151     */
152    public ViewElement parseParameter(Configuration paramConfiguration, ModelItemGroup parent, String pluginName, String catalog, View view, Map<String, ModelItem> modelItems, Model model, AbstractElementDefinitionParser<? extends DisableConditions> elementDefinitionParser, DataHolderRepeaterDefinitionParser repeaterDefinitionParser) throws ConfigurationException
153    {
154        ElementDefinition elementParameter = elementDefinitionParser.parse(_manager, pluginName, catalog, paramConfiguration, model, parent);
155        
156        if (elementParameter != null)
157        {
158            ViewElement viewElement = new ViewElement();
159            viewElement.setDefinition(elementParameter);
160            
161            return viewElement;
162        }
163        
164        return null;
165    }
166    
167    /**
168     * Parses a repeater
169     * @param repeaterConfiguration the repeater configuration
170     * @param parent the parent of the repeater. Can be <code>null</code> if the repeater has no parent
171     * @param pluginName the plugin name
172     * @param catalog the catalog
173     * @param view the view
174     * @param modelItems the map of model item
175     * @param model the model
176     * @param elementDefinitionParser the element definition parser
177     * @param repeaterDefinitionParser the repeater definition parser
178     * @return the parsed repeater
179     * @throws ConfigurationException if an error occurs
180     */
181    public ModelViewItemGroup parseRepeater(Configuration repeaterConfiguration, ModelItemGroup parent, String pluginName, String catalog, View view, Map<String, ModelItem> modelItems, Model model, AbstractElementDefinitionParser<? extends DisableConditions> elementDefinitionParser, DataHolderRepeaterDefinitionParser repeaterDefinitionParser) throws ConfigurationException
182    {
183        RepeaterDefinition repeaterDefinition = repeaterDefinitionParser.parse(_manager, pluginName, catalog, repeaterConfiguration, model, parent);
184        
185        if (repeaterDefinition != null)
186        {
187            ModelViewItemGroup<RepeaterDefinition> viewItemGroup = new ModelViewItemGroup<>();
188            viewItemGroup.setDefinition(repeaterDefinition);
189            
190            _parseChildren(repeaterConfiguration, viewItemGroup, repeaterDefinition, pluginName, catalog, view, modelItems, model, elementDefinitionParser, repeaterDefinitionParser);
191            
192            return viewItemGroup;
193        }
194        
195        return null;
196    }
197
198    /**
199     * Parses children of a view item container
200     * @param containerConfiguration the item container configuration
201     * @param viewItemContainer the item container
202     * @param modelParent The last model item container to use as parent for next definitions
203     * @param pluginName the plugin name
204     * @param catalog the catalog
205     * @param view the view
206     * @param modelItems the map of model items
207     * @param model the model
208     * @param elementDefinitionParser the element definition parser
209     * @param repeaterDefinitionParser the repeater definition parser
210     * @throws ConfigurationException if an error occurs
211     */
212    protected void _parseChildren(Configuration containerConfiguration, ViewItemContainer viewItemContainer, ModelItemGroup modelParent, String pluginName, String catalog, View view, Map<String, ModelItem> modelItems, Model model, AbstractElementDefinitionParser<? extends DisableConditions> elementDefinitionParser, DataHolderRepeaterDefinitionParser repeaterDefinitionParser) throws ConfigurationException
213    {
214        Configuration[] children = containerConfiguration.getChildren();
215        for (Configuration child : children)
216        {
217            ViewItem viewItem = null;
218            switch (child.getName())
219            {
220                case "fieldset":
221                    viewItem = _parseSimpleViewItemGroup(child, modelParent, pluginName, catalog, view, modelItems, model, elementDefinitionParser, repeaterDefinitionParser);
222                    ((ViewItemGroup) viewItem).setRole(ViewItemGroup.FIELDSET_ROLE);
223                    break;
224                case "parameter":
225                    viewItem = parseParameter(child, modelParent, pluginName, catalog, view, modelItems, model, elementDefinitionParser, repeaterDefinitionParser);
226                    break;
227                case "repeater":
228                    viewItem = parseRepeater(child, modelParent, pluginName, catalog, view, modelItems, model, elementDefinitionParser, repeaterDefinitionParser);
229                    break;
230                case "group":
231                    throw new ConfigurationException("This parameters configuration should not have subgroups", child);
232                default:
233                    break;
234            }
235            
236            if (viewItem != null)
237            {
238                viewItemContainer.addViewItem(viewItem);
239                if (viewItem instanceof ModelViewItem && modelParent == null)
240                {
241                    ModelItem definition = ((ModelViewItem) viewItem).getDefinition();
242                    String name = definition.getName();
243                    if (modelItems.containsKey(name))
244                    {
245                        getLogger().warn("Parameter with name '{}' is defined twice in the configuration. It will be ignored", name);
246                    }
247                    else
248                    {
249                        modelItems.put(name, definition);
250                    }
251                }
252            }
253        }
254    }
255    
256    /**
257     * Parse an i18n text.
258     * @param config the configuration to use.
259     * @param name the child name.
260     * @param pluginName the plugin name
261     * @param catalog the catalog
262     * @return the i18n text.
263     */
264    protected I18nizableText _parseI18nizableText(Configuration config, String name, String pluginName, String catalog)
265    {
266        String catalogName = StringUtils.isNotBlank(catalog) ? catalog : "plugin." + pluginName;
267        return I18nizableText.parseI18nizableText(config.getChild(name), catalogName, "");
268    }
269    
270    /**
271     * The view and parameters object
272     */
273    public static class ViewAndParameters
274    {
275        View _view;
276        Map<String, ModelItem> _modelItems;
277        
278        ViewAndParameters(View view, Map<String, ModelItem> modelItems)
279        {
280            this._view = view;
281            this._modelItems = modelItems;
282        }
283        
284        /**
285         * Get the view
286         * @return the view
287         */
288        public View getView()
289        {
290            return this._view;
291        }
292        
293        /**
294         * Get the map of model items
295         * @return the map of model items
296         */
297        public Map<String, ModelItem> getParameters()
298        {
299            return this._modelItems;
300        }
301    }
302}