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