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