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