001/*
002 *  Copyright 2019 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.config;
017
018import java.util.ArrayList;
019import java.util.Collection;
020import java.util.Comparator;
021import java.util.HashMap;
022import java.util.List;
023import java.util.Map;
024import java.util.Map.Entry;
025
026import org.ametys.runtime.i18n.I18nizableText;
027import org.ametys.runtime.model.CategorizedElementDefinitionHelper;
028import org.ametys.runtime.model.CategorizedElementDefinitionWrapper;
029import org.ametys.runtime.model.ElementDefinition;
030import org.ametys.runtime.model.ModelItem;
031import org.ametys.runtime.model.ModelItemGroup;
032import org.ametys.runtime.model.ModelViewItem;
033import org.ametys.runtime.model.ModelViewItemGroup;
034import org.ametys.runtime.model.View;
035import org.ametys.runtime.model.ViewElement;
036import org.ametys.runtime.model.ViewItemGroup;
037
038/**
039 * Helper for {@link ConfigParameterDefinitionWrapper}
040 */
041public final class ConfigParameterDefinitionHelper
042{
043    private ConfigParameterDefinitionHelper()
044    {
045        //Nothing
046    }
047    
048    /**
049     * Get definitions of all elements parsed in this {@link CategorizedElementDefinitionWrapper} {@link Collection}, as a flat map.
050     * @param elements a {@link Collection} of {@link CategorizedElementDefinitionWrapper}
051     * @return a map with the definition name as key, containing all definitions
052     */
053    public static Map<String, ElementDefinition> getFlatDefinitions(Collection<ConfigParameterDefinitionWrapper> elements)
054    {
055        Map<String, ElementDefinition> flatDefinitions = new HashMap<>();
056        for (ConfigParameterDefinitionWrapper element : elements)
057        {
058            ElementDefinition definition = element.getDefinition();
059            flatDefinitions.put(definition.getName(), definition);
060        }
061        return flatDefinitions;
062    }
063    
064    /**
065     * Categorize the ModlItems based on the informations in the {@link CategorizedElementDefinitionWrapper} collection
066     * Category and groups are not sorted, only definitions, based on the position.
067     * Sorting of category and groups have to be done when the view is created (each usage can be different)
068     * @param elements collection of categorized elements
069     * @return a list of {@link ModelItem} with the correct tree (category/group/definitions)
070     */
071    public static List<ModelItem> categorizeConfigParameters(Collection<ConfigParameterDefinitionWrapper> elements)
072    {
073        Map<I18nizableText, Map<I18nizableText, List<ConfigParameterDefinitionWrapper>>> categories = CategorizedElementDefinitionHelper.categorizeElementDefinitionWrappers(elements);
074        //This list is not sorted, only definition are sorted, not groups.
075        //Groups should be sorted elsewhere (when creating the view for example)
076        List<ModelItem> modelItems = new ArrayList<>();
077        for (Entry<I18nizableText, Map<I18nizableText, List<ConfigParameterDefinitionWrapper>>> categoryEntry : categories.entrySet())
078        {
079            ModelItemGroup category = new ModelItemGroup();
080            category.setLabel(categoryEntry.getKey());
081            modelItems.add(category);
082            Map<I18nizableText, List<ConfigParameterDefinitionWrapper>> values = categoryEntry.getValue();
083            for (Entry<I18nizableText, List<ConfigParameterDefinitionWrapper>> groupEntry : values.entrySet())
084            {
085                ModelItemGroup group = new ModelItemGroup();
086                group.setLabel(groupEntry.getKey());
087                category.addChild(group);
088                
089                for (ConfigParameterDefinitionWrapper orderedDefinition : groupEntry.getValue())
090                {
091                    ElementDefinition definition = orderedDefinition.getDefinition();
092                    definition.setParent(group);
093                    
094                    group.addChild(definition, orderedDefinition.isGroupSwitch());
095                }
096            }
097        }
098        return modelItems;
099    }
100    
101    /**
102     * Generate the view for a list of {@link ModelItem}, using comparators to sort the categories and the groups
103     * This works only on the very specific categories/group/fieldset hierarchy and an {@link IllegalArgumentException} will be thrown if the hierarchy is incorrect.
104     * @param categories list of {@link ModelItem}
105     * @param categoriesComparator {@link Comparator} used for the categories (can be null to keep order)
106     * @param groupsComparator {@link Comparator} for the groups (can be null to keep order)
107     * @param elementsComparator {@link Comparator} for the elements (can be null to keep order)
108     * @return A {@link View} ordered from the {@link ModelItem} using the {@link Comparator}
109     * @throws IllegalArgumentException the hierarchy of the categories/groups/elements is incorrect
110     */
111    public static View buildConfigParametersView(Collection<? extends ModelItem> categories, Comparator<? super ModelItem> categoriesComparator, Comparator<? super ModelItem> groupsComparator, Comparator<? super ModelItem> elementsComparator) throws IllegalArgumentException
112    {
113        View view = new View();
114        
115        Collection<? extends ModelItem> sorted = _sort(categories, categoriesComparator);
116        
117        for (ModelItem modelItem : sorted)
118        {
119            view.addViewItem(_buildCategoryViewItem(modelItem, groupsComparator, elementsComparator));
120        }
121        return view;
122    }
123    
124    private static ModelViewItemGroup _buildCategoryViewItem(ModelItem modelItem, Comparator<? super ModelItem> groupsComparator, Comparator<? super ModelItem> elementsComparator) throws IllegalArgumentException
125    {
126        if (modelItem instanceof ModelItemGroup)
127        {
128            ModelItemGroup category = (ModelItemGroup) modelItem;
129            ModelViewItemGroup categoryViewItem = new ModelViewItemGroup();
130            categoryViewItem.setRole(ViewItemGroup.TAB_ROLE);
131            categoryViewItem.setDefinition(category);
132            
133            Collection<? extends ModelItem> groups = _sort(category.getChildren(), groupsComparator);
134            
135            for (ModelItem group : groups)
136            {
137                categoryViewItem.addViewItem(_buildGroupViewItem(group, elementsComparator));
138            }
139            return categoryViewItem;
140        }
141        else
142        {
143            throw new IllegalArgumentException("Category " + modelItem.getPath() + " should be an instance of ModelItemGroup");
144        }
145    }
146    
147    private static ModelViewItemGroup _buildGroupViewItem(ModelItem modelItem, Comparator<? super ModelItem> elementsComparator) throws IllegalArgumentException
148    {
149        if (modelItem instanceof ModelItemGroup)
150        {
151            ModelItemGroup group = (ModelItemGroup) modelItem;
152            ModelViewItemGroup groupViewItem = new ModelViewItemGroup();
153            groupViewItem.setRole(ViewItemGroup.FIELDSET_ROLE);
154            groupViewItem.setDefinition(group);
155            
156            Collection<? extends ModelItem> items = _sort(group.getChildren(), elementsComparator);
157            
158            for (ModelItem item : items)
159            {
160                groupViewItem.addViewItem(_buildElementViewItem(item));
161            }
162            return groupViewItem;
163        }
164        else
165        {
166            throw new IllegalArgumentException("Group " + modelItem.getPath() + " should be an instance of ModelItemGroup");
167        }
168    }
169    
170    private static ModelViewItem _buildElementViewItem(ModelItem modelItem) throws IllegalArgumentException
171    {
172        if (modelItem instanceof ElementDefinition)
173        {
174            ElementDefinition definition = (ElementDefinition) modelItem;
175            
176            ViewElement parameterViewItem = new ViewElement();
177            parameterViewItem.setDefinition(definition);
178            return parameterViewItem;
179        }
180        else
181        {
182            throw new IllegalArgumentException("Item " + modelItem.getPath() + " should be an instance of ElementDefinition");
183        }
184    }
185    
186    /**
187     * sort a model item list using comparator
188     * @param items list of items to sort
189     * @param comparator a comparator, can be null to avoid sort
190     * @return a sorted list (or a copy of the list if the comparator is null)
191     */
192    private static Collection<? extends ModelItem> _sort(Collection<? extends ModelItem> items, Comparator<? super ModelItem> comparator)
193    {
194        if (comparator == null)
195        {
196            return items;
197        }
198        else
199        {
200            List<ModelItem> sorted = new ArrayList<>(items);
201            sorted.sort(comparator);
202            return sorted;
203        }
204    }
205}