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 = sortItemsList(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 = sortItemsList(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 = sortItemsList(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    public static <T> Collection<T> sortItemsList(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 values a map of all parameters and their values
160     * @param definitions collection of {@link ElementDefinition} from the model file
161     * @param disableConditionsEvaluator the disable conditions evaluator for the given object 
162     * @param logger a logger
163     * @return a map containing the potential errors
164     */
165    public static ValidationResults validateValuesForWriting (Map<String, Object> values, Collection<? extends ElementDefinition> definitions, DisableConditionsEvaluator disableConditionsEvaluator, Logger logger)
166    {
167        ValidationResults results = new ValidationResults();
168        
169        for (ElementDefinition definition : definitions)
170        {
171            boolean isGroupSwitchOn = ModelHelper.isGroupSwitchOn(definition, values);
172            boolean isDisabled = disableConditionsEvaluator.evaluateDisableConditions(definition, definition.getName(), values);
173            
174            if (isGroupSwitchOn && !isDisabled)
175            {
176                Object value = values.get(definition.getName());
177                ValidationResult result = ModelHelper.validateValue(definition, value);
178                results.addResult(definition.getName(), result);
179                
180                if (result.hasErrors())
181                {
182                    logger.warn("The configuration parameter '{}' is not valid", definition.getName());
183                }
184            }
185        }
186        
187        if (results.hasErrors())
188        {
189            logger.debug("Failed to save configuration because of invalid parameter values");
190        }
191        
192        return results;
193    }
194    
195    /**
196     * Compares the two given wrappers' position.
197     * Returns a negative integer, zero, or a positive integer as the position of the first wrapper
198     * is less than, equal to, or greater than the second.
199     * @param <T> type of the wrappers to compare
200     * @param wrapper1 the first wrapper
201     * @param wrapper2 the second wrapper
202     * @return a negative integer, zero, or a positive integer as the position of the first wrapper
203     * is less than, equal to, or greater than the second.
204     */
205    public static <T extends CategorizedElementDefinitionWrapper> int compareWrapperPositions(T wrapper1, T wrapper2)
206    {
207        return comparePositions(wrapper1.getPosition(), wrapper2.getPosition());
208    }
209    
210    /**
211     * Compares the two given position.
212     * Returns a negative integer, zero, or a positive integer as the first position is less than, equal to, or greater than the second.
213     * @param position1 the first position
214     * @param position2 the second position
215     * @return a negative integer, zero, or a positive integer as the first position is less than, equal to, or greater than the second.
216     */
217    public static int comparePositions(Long position1, Long position2)
218    {
219        int positionComparison = position1.compareTo(position2);
220        if (position1 < 0 && position2 < 0)
221        {
222            positionComparison = 0;
223        }
224        else if (position1 < 0 && position2 >= 0)
225        {
226            positionComparison = 1;
227        }
228        else if (position2 < 0)
229        {
230            positionComparison = -1;
231        }
232        
233        return positionComparison;
234    }
235}