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