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