001/* 002 * Copyright 2024 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.HashMap; 020import java.util.List; 021import java.util.Map; 022import java.util.Map.Entry; 023 024import org.apache.avalon.framework.configuration.Configuration; 025import org.apache.avalon.framework.configuration.ConfigurationException; 026import org.apache.commons.lang3.StringUtils; 027import org.apache.commons.lang3.tuple.ImmutablePair; 028import org.apache.commons.lang3.tuple.Pair; 029import org.slf4j.Logger; 030import org.slf4j.LoggerFactory; 031 032import org.ametys.plugins.core.ui.util.ConfigurationHelper; 033import org.ametys.runtime.config.ConfigManager; 034import org.ametys.runtime.i18n.I18nizableText; 035import org.ametys.runtime.model.type.ElementType; 036 037/** 038 * Helper for all {@link ModelItem} parsers 039 */ 040public final class ItemParserHelper 041{ 042 private static final Logger __LOGGER = LoggerFactory.getLogger(ModelHelper.class); 043 044 private ItemParserHelper() 045 { 046 // Empty constructor 047 } 048 049 /** 050 * Parses the name of the model item 051 * @param itemConfig the model item's configuration to parse 052 * @param nameConfigurationAttribute the name of the attribute containing the item's name 053 * @return the name of the model item 054 * @throws ConfigurationException if an error occurs 055 */ 056 public static String parseName(Configuration itemConfig, String nameConfigurationAttribute) throws ConfigurationException 057 { 058 return parseName(itemConfig, nameConfigurationAttribute, true); 059 } 060 061 /** 062 * Parses the name of the model item 063 * @param itemConfig the model item's configuration to parse 064 * @param nameConfigurationAttribute the name of the attribute containing the item's name 065 * @param acceptDots <code>true</code> if the name to parse can contain dots, <code>false</code> otherwise 066 * @return the name of the model item 067 * @throws ConfigurationException if an error occurs 068 */ 069 public static String parseName(Configuration itemConfig, String nameConfigurationAttribute, boolean acceptDots) throws ConfigurationException 070 { 071 String itemName = itemConfig.getAttribute(nameConfigurationAttribute); 072 073 String regex = acceptDots 074 ? "^[a-zA-Z]((?!__)[a-zA-Z0-9_\\.-])*$" 075 : "^[a-zA-Z]((?!__)[a-zA-Z0-9_-])*$"; 076 if (!itemName.matches(regex)) 077 { 078 StringBuilder message = new StringBuilder() 079 .append("Invalid model item name: ").append(itemName).append(".") 080 .append(" The item name must start with a letter and must contain only letters, digits, underscores").append(acceptDots ? ", dots " : " ").append("or dashes."); 081 throw new ConfigurationException(message.toString(), itemConfig); 082 } 083 084 return itemName; 085 } 086 087 /** 088 * Parses the multiple attribute. 089 * @param itemConfig the item configuration to use. 090 * @return <code>true</code> if the item is multiple, <code>false</code> otherwise. 091 * @throws ConfigurationException if the configuration is not valid. 092 */ 093 public static Boolean parseMultiple(Configuration itemConfig) throws ConfigurationException 094 { 095 return itemConfig.getAttributeAsBoolean("multiple", false); 096 } 097 098 /** 099 * Parses the default values. 100 * @param itemConfig the item configuration. 101 * @param definition the element definition. 102 * @param defaultValueParser The {@link DefaultValueParser} to use to parse single default values 103 * @return the default values and their types or <code>null</code> if none default value is defined. 104 * @throws ConfigurationException if the configuration is not valid. 105 */ 106 public static List<Pair<String, Object>> parseDefaultValues(Configuration itemConfig, ElementDefinition definition, DefaultValueParser defaultValueParser) throws ConfigurationException 107 { 108 Configuration[] defaultValueConfigs = itemConfig.getChildren("default-value"); 109 if (defaultValueConfigs.length > 0) 110 { 111 List<Pair<String, Object>> defaultValues = new ArrayList<>(); 112 113 for (Configuration defaultValueConfig : defaultValueConfigs) 114 { 115 String defaultValueType = defaultValueConfig.getAttribute("type", null); 116 Object defaultValue = defaultValueParser.parseDefaultValue(defaultValueConfig, definition, defaultValueType); 117 defaultValues.add(new ImmutablePair<>(defaultValueType, defaultValue)); 118 } 119 120 return defaultValues; 121 } 122 else 123 { 124 return null; 125 } 126 } 127 128 /** 129 * Parses the default value. 130 * @param defaultValueConfig the default value configuration. 131 * @param definition the element definition. 132 * @param defaultValueType the type of the default value 133 * @return the default value or <code>null</code> if none default value is defined. 134 * @throws ConfigurationException if the configuration is not valid. 135 */ 136 public static Object parseDefaultValue(Configuration defaultValueConfig, ElementDefinition definition, String defaultValueType) throws ConfigurationException 137 { 138 ElementType type = definition.getType(); 139 if (defaultValueType != null) 140 { 141 if (ElementDefinition.CONFIG_DEFAULT_VALUE_TYPE.equals(defaultValueType)) 142 { 143 String configParamName = defaultValueConfig.getValue(); 144 if (ConfigManager.getInstance().hasModelItem(configParamName)) 145 { 146 ModelItem configParamDefinition = ConfigManager.getInstance().getModelItem(configParamName); 147 String configParamTypeId = configParamDefinition.getType().getId(); 148 if (configParamTypeId.equals(type.getId())) 149 { 150 return configParamName; 151 } 152 else 153 { 154 throw new ConfigurationException("The configuration parameter '" + configParamName + " (" + configParamTypeId + ")' cannot be used as default value for item '" + definition.getPath() + " (" + type.getId() + ")': types are not the same.", defaultValueConfig); 155 } 156 } 157 else 158 { 159 throw new ConfigurationException("The configuration parameter '" + configParamName + "' does not exist, it cannot be used as default value for item '" + definition.getPath() + " (" + type.getId() + ")'.", defaultValueConfig); 160 } 161 } 162 else 163 { 164 throw new ConfigurationException("The type '" + defaultValueType + "' is not available for the default value of item '" + definition.getPath() + " (" + type.getId() + ")'.", defaultValueConfig); 165 } 166 } 167 else 168 { 169 return type.parseConfiguration(defaultValueConfig); 170 } 171 } 172 173 /** 174 * Parses the widget. 175 * @param itemConfig the item configuration to use. 176 * @return the widget or <code>null</code> if none defined. 177 * @throws ConfigurationException if the configuration is not valid. 178 */ 179 public static String parseWidget(Configuration itemConfig) throws ConfigurationException 180 { 181 return itemConfig.getChild("widget").getValue(null); 182 } 183 184 /** 185 * Parses the widget's parameters 186 * @param itemConfig the item configuration to use. 187 * @param pluginName the current plugin name. 188 * @return the widget's parameters in a Map 189 * @throws ConfigurationException if the configuration is not valid. 190 */ 191 public static Map<String, I18nizableText> parseWidgetParameters(Configuration itemConfig, String pluginName) throws ConfigurationException 192 { 193 Map<String, I18nizableText> widgetParams = new HashMap<>(); 194 195 Configuration widgetParamsConfig = itemConfig.getChild("widget-params", false); 196 if (widgetParamsConfig != null) 197 { 198 Map<String, Object> parsedParams = ConfigurationHelper.parsePluginParameters(widgetParamsConfig, pluginName, __LOGGER); 199 200 for (Entry<String, Object> param : parsedParams.entrySet()) 201 { 202 String paramName = param.getKey(); 203 Object value = param.getValue(); 204 if (value instanceof I18nizableText) 205 { 206 widgetParams.put(paramName, (I18nizableText) value); 207 } 208 else if (value instanceof String) 209 { 210 widgetParams.put(paramName, new I18nizableText((String) value)); 211 } 212 else 213 { 214 __LOGGER.warn("Widget parameter '{}' at location {} is of type [{}] which is not supported. It will be ignored.", paramName, itemConfig.getLocation(), value.getClass()); 215 } 216 } 217 } 218 219 return widgetParams; 220 } 221 222 /** 223 * Parse an i18n text. 224 * @param configurationAndPluginName the configuration to use. 225 * @param name the child name. 226 * @return the i18n text. 227 * @throws ConfigurationException if the configuration is not valid. 228 */ 229 public static I18nizableText parseI18nizableText(ConfigurationAndPluginName configurationAndPluginName, String name) throws ConfigurationException 230 { 231 return parseI18nizableText(configurationAndPluginName, name, StringUtils.EMPTY); 232 } 233 234 /** 235 * Parse an i18n text. 236 * @param configurationAndPluginName the configuration to use. 237 * @param name the child name. 238 * @param defaultValue the default value if no present 239 * @return the i18n text. 240 * @throws ConfigurationException if the configuration is not valid. 241 */ 242 public static I18nizableText parseI18nizableText(ConfigurationAndPluginName configurationAndPluginName, String name, String defaultValue) throws ConfigurationException 243 { 244 return I18nizableText.parseI18nizableText(configurationAndPluginName.configuration().getChild(name), "plugin." + configurationAndPluginName.pluginName(), defaultValue); 245 } 246 247 /** 248 * Parse an i18n text. 249 * @param configurationAndPluginName the configuration to use. 250 * @param name the child name. 251 * @param defaultValue the default value if no present 252 * @return the i18n text. 253 * @throws ConfigurationException if the configuration is not valid. 254 */ 255 public static I18nizableText parseI18nizableText(ConfigurationAndPluginName configurationAndPluginName, String name, I18nizableText defaultValue) throws ConfigurationException 256 { 257 return I18nizableText.parseI18nizableText(configurationAndPluginName.configuration().getChild(name), "plugin." + configurationAndPluginName.pluginName(), defaultValue); 258 } 259 260 /** 261 * Stores a configuration and the name of the plugin in which is declared the configuration 262 * @param configuration the configuration itself 263 * @param pluginName the name of the plugin in which is declared the configuration 264 */ 265 public record ConfigurationAndPluginName(Configuration configuration, String pluginName) { /* empty */ } 266 267 /** 268 * Default value parser 269 */ 270 @FunctionalInterface 271 public interface DefaultValueParser 272 { 273 /** 274 * Parses the default value. 275 * @param defaultValueConfig the default value configuration. 276 * @param definition the element definition. 277 * @param defaultValueType the type of the default value 278 * @return the default value or <code>null</code> if none default value is defined. 279 * @throws ConfigurationException if the configuration is not valid. 280 */ 281 public Object parseDefaultValue(Configuration defaultValueConfig, ElementDefinition definition, String defaultValueType) throws ConfigurationException; 282 } 283}