001/*
002 *  Copyright 2018 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.model;
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;
024
025import org.slf4j.Logger;
026
027import org.ametys.runtime.i18n.I18nizableText;
028import org.ametys.runtime.model.disableconditions.DisableConditionsEvaluator;
029import org.ametys.runtime.parameter.ValidationResult;
030import org.ametys.runtime.parameter.ValidationResults;
031
032/**
033 * Helper for {@link CategorizedElementDefinitionWrapper}
034 */
035public final class CategorizedElementDefinitionHelper
036{
037    private CategorizedElementDefinitionHelper()
038    {
039        //Nothing
040    }
041    
042    /**
043     * Generate the view for categorized element definition wrappers, using comparators to sort the categories, groups and elements
044     * @param <T> type of the wrappers
045     * @param wrappers the wrappers
046     * @param categoriesComparator {@link Comparator} used for the categories (can be null to keep order)
047     * @param groupsComparator {@link Comparator} for the groups (can be null to keep order)
048     * @param elementsComparator {@link Comparator} for the elements (can be null to keep order)
049     * @return A {@link View} ordered from the {@link ModelItem} using the {@link Comparator}
050     */
051    public static <T extends CategorizedElementDefinitionWrapper> View buildViewFromCategories(Collection<T> wrappers, Comparator<I18nizableText> categoriesComparator, Comparator<I18nizableText> groupsComparator, Comparator<T> elementsComparator)
052    {
053        View view = new View();
054        
055        Map<I18nizableText, Map<I18nizableText, List<T>>> categories = categorizeElementDefinitionWrappers(wrappers);
056        
057        Collection<I18nizableText> sortedCategories = _sort(categories.keySet(), categoriesComparator);
058        for (I18nizableText categoryLabel : sortedCategories)
059        {
060            ViewItem categoryViewItem = _buildCategoryViewItem(categoryLabel, categories.get(categoryLabel), groupsComparator, elementsComparator);
061            view.addViewItem(categoryViewItem);
062        }
063        return view;
064    }
065    
066    /**
067     * Organize a collection of categorized element definition wrappers by categories and groups.
068     * @param <T> type of the wrappers
069     * @param wrappers a collection of element definition wrappers.
070     * @return a Map with the same element definition wrappers, sorted first by category then by group.
071     */
072    public static <T extends CategorizedElementDefinitionWrapper> Map<I18nizableText, Map<I18nizableText, List<T>>> categorizeElementDefinitionWrappers(Collection<T> wrappers)
073    {
074        Map<I18nizableText, Map<I18nizableText, List<T>>> categories = new HashMap<>();
075
076        // Classify parameters by groups and categories
077        for (T parameter : wrappers)
078        {
079            I18nizableText displayCategory = parameter.getDisplayCategory();
080            I18nizableText displayGroup = parameter.getDisplayGroup();
081
082            // Get the map of groups of the category
083            Map<I18nizableText, List<T>> category = categories.get(displayCategory);
084            if (category == null)
085            {
086                category = new HashMap<>();
087                categories.put(displayCategory, category);
088            }
089
090            // Get the map of parameters of the group
091            List<T> group = category.get(displayGroup);
092            if (group == null)
093            {
094                group = new ArrayList<>();
095                category.put(displayGroup, group);
096            }
097
098            group.add(parameter);
099        }
100        
101        return categories;
102    }
103    
104    private static <T extends CategorizedElementDefinitionWrapper> ViewItem _buildCategoryViewItem(I18nizableText categoryLabel, Map<I18nizableText, List<T>> groups, Comparator<I18nizableText> groupsComparator, Comparator<T> elementsComparator) throws IllegalArgumentException
105    {
106        SimpleViewItemGroup categoryViewItem = new SimpleViewItemGroup();
107        categoryViewItem.setRole(ViewItemGroup.TAB_ROLE);
108        categoryViewItem.setLabel(categoryLabel);
109
110        Collection<I18nizableText> sortedGroups = _sort(groups.keySet(), groupsComparator);
111        for (I18nizableText groupLabel : sortedGroups)
112        {
113            ViewItem groupViewItem = _buildGroupViewItem(groupLabel, groups.get(groupLabel), elementsComparator);
114            categoryViewItem.addViewItem(groupViewItem);
115        }
116        return categoryViewItem;
117    }
118    
119    private static <T extends CategorizedElementDefinitionWrapper> ViewItem _buildGroupViewItem(I18nizableText groupLabel, List<T> wrappers, Comparator<T> elementsComparator) throws IllegalArgumentException
120    {
121        SimpleViewItemGroup groupViewItem = new SimpleViewItemGroup();
122        groupViewItem.setRole(ViewItemGroup.FIELDSET_ROLE);
123        groupViewItem.setLabel(groupLabel);
124
125        Collection<T> items = _sort(wrappers, elementsComparator);
126        for (T item : items)
127        {
128            ViewElement parameterViewItem = new ViewElement();
129            parameterViewItem.setDefinition(item.getDefinition());
130            groupViewItem.addViewItem(parameterViewItem);
131        }
132        
133        return groupViewItem;
134    }
135    
136    /**
137     * Sort a model item list using comparator
138     * @param <T> The type of item to compare
139     * @param items list of items to sort
140     * @param comparator a comparator, can be null to avoid sort
141     * @return a sorted list (or a copy of the list if the comparator is null)
142     */
143    private static <T> Collection<T> _sort(Collection<T> items, Comparator<T> comparator)
144    {
145        if (comparator == null)
146        {
147            return items;
148        }
149        else
150        {
151            List<T> sorted = new ArrayList<>(items);
152            sorted.sort(comparator);
153            return sorted;
154        }
155    }
156    
157    /**
158     * Validate parameters before writing
159     * @param <T> Type of object containing the values
160     * @param values a map of all parameters and their values
161     * @param definitions collection of {@link ElementDefinition} from the model file
162     * @param disableConditionsEvaluator the disable conditions evaluator for the given object 
163     * @param logger a logger
164     * @return a map containing the potential errors
165     */
166    public static <T> ValidationResults validateValuesForWriting (Map<String, Object> values, Collection<ElementDefinition> definitions, DisableConditionsEvaluator<T> disableConditionsEvaluator, Logger logger)
167    {
168        ValidationResults results = new ValidationResults();
169        
170        for (ElementDefinition definition : definitions)
171        {
172            boolean isGroupSwitchOn = ModelHelper.isGroupSwitchOn(definition, values);
173            boolean isDisabled = disableConditionsEvaluator.evaluateDisableConditions(definition, definition.getName(), values);
174            
175            if (isGroupSwitchOn && !isDisabled)
176            {
177                Object value = values.get(definition.getName());
178                ValidationResult result = ModelHelper.validateValue(definition, value);
179                results.addResult(definition.getName(), result);
180                
181                if (result.hasErrors())
182                {
183                    logger.warn("The configuration parameter '{}' is not valid", definition.getName());
184                }
185            }
186        }
187        
188        if (results.hasErrors())
189        {
190            logger.debug("Failed to save configuration because of invalid parameter values");
191        }
192        
193        return results;
194    }
195    
196    /**
197     * Compares the two given wrappers' position.
198     * Returns a negative integer, zero, or a positive integer as the position of the first wrapper
199     * is less than, equal to, or greater than the second.
200     * @param <T> type of the wrappers to compare
201     * @param wrapper1 the first wrapper
202     * @param wrapper2 the second wrapper
203     * @return a negative integer, zero, or a positive integer as the position of the first wrapper
204     * is less than, equal to, or greater than the second.
205     */
206    public static <T extends CategorizedElementDefinitionWrapper> int compareWrapperPositions(T wrapper1, T wrapper2)
207    {
208        int positionComparison = ((Long) wrapper1.getPosition()).compareTo(wrapper2.getPosition());
209        if (wrapper1.getPosition() < 0 && wrapper2.getPosition() < 0)
210        {
211            positionComparison = 0;
212        }
213        else if (wrapper1.getPosition() < 0 && wrapper2.getPosition() >= 0)
214        {
215            positionComparison = 1;
216        }
217        else if (wrapper2.getPosition() < 0)
218        {
219            positionComparison = -1;
220        }
221        
222        return positionComparison;
223    }
224}