001/* 002 * Copyright 2020 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.plugins.repository.model; 017 018import java.util.ArrayList; 019import java.util.List; 020import java.util.Map; 021import java.util.Optional; 022import java.util.function.BiConsumer; 023import java.util.function.Consumer; 024 025import org.apache.commons.lang3.StringUtils; 026 027import org.ametys.runtime.i18n.I18nizableText; 028import org.ametys.runtime.model.ElementDefinition; 029import org.ametys.runtime.model.ModelHelper; 030import org.ametys.runtime.model.ModelItem; 031import org.ametys.runtime.model.ModelItemGroup; 032import org.ametys.runtime.model.ModelViewItemGroup; 033import org.ametys.runtime.model.ViewElement; 034import org.ametys.runtime.model.ViewItem; 035import org.ametys.runtime.model.ViewItemAccessor; 036import org.ametys.runtime.model.ViewItemGroup; 037import org.ametys.runtime.parameter.ValidationResult; 038import org.ametys.runtime.parameter.ValidationResults; 039 040/** 041 * Helper for manipulating views in the context of the repository plugin (aware of repeaters, composites, ...). 042 */ 043public final class ViewHelper 044{ 045 private ViewHelper() 046 { 047 // Empty constructor 048 } 049 050 /** 051 * Visit a view, allowing to perform specific actions for view elements. 052 * @param viewItemAccessor the {@link ViewItemAccessor} to visit. 053 * @param elementConsumer the consumer called on each {@link ViewElement}. 054 * @param compositeConsumer the consumer called on each item refering to a {@link CompositeDefinition}. 055 * @param repeaterConsumer the consumer called on each item refering to a {@link RepeaterDefinition}. 056 * @param groupConsumer the consumer called on each other {@link ViewItemGroup}. 057 */ 058 public static void visitView(ViewItemAccessor viewItemAccessor, BiConsumer<ViewElement, ElementDefinition> elementConsumer, BiConsumer<ModelViewItemGroup, CompositeDefinition> compositeConsumer, BiConsumer<ModelViewItemGroup, RepeaterDefinition> repeaterConsumer, Consumer<ViewItemGroup> groupConsumer) 059 { 060 for (ViewItem viewItem : viewItemAccessor.getViewItems()) 061 { 062 if (viewItem instanceof ViewElement) 063 { 064 ElementDefinition definition = ((ViewElement) viewItem).getDefinition(); 065 elementConsumer.accept((ViewElement) viewItem, definition); 066 } 067 else if (viewItem instanceof ModelViewItemGroup) 068 { 069 ModelItemGroup modelItemGroup = ((ModelViewItemGroup) viewItem).getDefinition(); 070 071 if (modelItemGroup instanceof CompositeDefinition) 072 { 073 compositeConsumer.accept((ModelViewItemGroup) viewItem, (CompositeDefinition) modelItemGroup); 074 } 075 else if (modelItemGroup instanceof RepeaterDefinition) 076 { 077 repeaterConsumer.accept((ModelViewItemGroup) viewItem, (RepeaterDefinition) modelItemGroup); 078 } 079 } 080 else if (viewItem instanceof ViewItemGroup) 081 { 082 groupConsumer.accept((ViewItemGroup) viewItem); 083 } 084 } 085 } 086 087 /** 088 * Validates the given values according to the view item accessor 089 * @param values the values to validate 090 * @param viewItemAccessor the view item accessor to visit 091 * @return the errors information if the validation fails. 092 */ 093 public static ValidationResults validateValues(ViewItemAccessor viewItemAccessor, Optional<Map<String, Object>> values) 094 { 095 return _validateValues(viewItemAccessor, values, StringUtils.EMPTY); 096 } 097 098 private static ValidationResults _validateValues(ViewItemAccessor viewItemAccessor, Optional<Map<String, Object>> values, String prefix) 099 { 100 ValidationResults results = new ValidationResults(); 101 102 visitView(viewItemAccessor, 103 (element, definition) -> { 104 // simple element 105 String name = definition.getName(); 106 String dataPath = prefix + name; 107 108 Object value = values.map(v -> v.get(name)).orElse(null); 109 ValidationResult result = ModelHelper.validateValue(definition, value); 110 results.addResult(dataPath, result); 111 }, 112 (group, definition) -> { 113 // composite 114 String name = definition.getName(); 115 String updatedPrefix = prefix + name + ModelItem.ITEM_PATH_SEPARATOR; 116 117 Optional<Map<String, Object>> value = values.map(v -> v.get(name)).filter(Map.class::isInstance).map(Map.class::cast); 118 results.addResults(_validateValues(group, value, updatedPrefix)); 119 }, 120 (group, definition) -> { 121 // repeater 122 String name = definition.getName(); 123 String dataPath = prefix + name; 124 125 Optional<List<Map<String, Object>>> entries = values.map(v -> v.get(name)).filter(List.class::isInstance).map(List.class::cast); 126 results.addResults(_validateRepeaterEntries(group, definition, entries, dataPath)); 127 }, 128 group -> { 129 results.addResults(_validateValues(group, values, prefix)); 130 }); 131 132 return results; 133 } 134 135 private static ValidationResults _validateRepeaterEntries(ModelViewItemGroup viewItem, RepeaterDefinition definition, Optional<List<Map<String, Object>>> entries, String dataPath) 136 { 137 ValidationResults results = new ValidationResults(); 138 139 int repeaterSize = entries.map(List::size).orElse(0); 140 List<I18nizableText> repeaterSizeErrors = _validateRepeaterSize(definition, repeaterSize); 141 if (!repeaterSizeErrors.isEmpty()) 142 { 143 ValidationResult repeaterResult = new ValidationResult(); 144 repeaterResult.addErrors(repeaterSizeErrors); 145 results.addResult(dataPath, repeaterResult); 146 } 147 else if (entries.isPresent()) 148 { 149 for (int i = 0; i < repeaterSize; i++) 150 { 151 Map<String, Object> entry = entries.get().get(i); 152 String prefix = dataPath + "[" + (i + 1) + "]" + ModelItem.ITEM_PATH_SEPARATOR; 153 results.addResults(_validateValues(viewItem, Optional.of(entry), prefix)); 154 } 155 } 156 157 return results; 158 } 159 160 private static List<I18nizableText> _validateRepeaterSize(RepeaterDefinition definition, int repeaterSize) 161 { 162 int minSize = definition.getMinSize(); 163 int maxSize = definition.getMaxSize(); 164 165 List<I18nizableText> errors = new ArrayList<>(); 166 167 if (repeaterSize < minSize) 168 { 169 List<String> parameters = new ArrayList<>(); 170 parameters.add(definition.getName()); 171 parameters.add(Integer.toString(minSize)); 172 errors.add(new I18nizableText("plugin.cms", "CONTENT_EDITION_VALIDATION_ERRORS_REPEATER_MINSIZE", parameters)); 173 } 174 175 if (maxSize > 0 && repeaterSize > maxSize) 176 { 177 List<String> parameters = new ArrayList<>(); 178 parameters.add(definition.getName()); 179 parameters.add(Integer.toString(maxSize)); 180 errors.add(new I18nizableText("plugin.cms", "CONTENT_EDITION_VALIDATION_ERRORS_REPEATER_MAXSIZE", parameters)); 181 } 182 183 return errors; 184 } 185}