001/* 002 * Copyright 2014 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.parameter; 017 018import java.io.InputStream; 019import java.time.LocalDateTime; 020import java.time.ZonedDateTime; 021import java.time.chrono.IsoChronology; 022import java.time.format.DateTimeFormatter; 023import java.time.format.ResolverStyle; 024import java.util.ArrayList; 025import java.util.Collection; 026import java.util.Date; 027import java.util.HashMap; 028import java.util.List; 029import java.util.Map; 030 031import org.apache.cocoon.ProcessingException; 032import org.apache.cocoon.xml.AttributesImpl; 033import org.apache.cocoon.xml.XMLUtils; 034import org.slf4j.Logger; 035import org.slf4j.LoggerFactory; 036import org.xml.sax.ContentHandler; 037import org.xml.sax.SAXException; 038 039import org.ametys.core.util.DateUtils; 040import org.ametys.runtime.i18n.I18nizableText; 041 042 043/** 044 * This class handles all needed to use typed parameters 045 */ 046public final class ParameterHelper 047{ 048 /** Enumeration of supported types */ 049 public static enum ParameterType 050 { 051 /** boolean values */ 052 BOOLEAN, 053 /** string values */ 054 STRING, 055 /** password values */ 056 PASSWORD, 057 /** long values */ 058 LONG, 059 /** double values */ 060 DOUBLE, 061 /** date values */ 062 DATE, 063 /** binary values */ 064 BINARY, 065 /** datasource values */ 066 DATASOURCE 067 } 068 069 /** 070 * The ISO date-time formatter that formats or parses a date-time with an offset, such as '2011-12-03T10:15:30.000+01:00'. 071 */ 072 private static DateTimeFormatter __ISO_OFFSET_DATE_TIME = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX").withResolverStyle(ResolverStyle.STRICT).withChronology(IsoChronology.INSTANCE); 073 074 // Logger for traces 075 private static Logger _logger = LoggerFactory.getLogger(ParameterHelper.class); 076 077 private ParameterHelper () 078 { 079 // empty 080 } 081 082 /** 083 * Get the ISO date-time formatter that formats or parses a date-time with an offset, such as '2011-12-03T10:15:30.000+01:00'. 084 * This formatter is similar to {@link DateTimeFormatter#ISO_OFFSET_DATE_TIME} but force 3-digits milliseconds. 085 * @return ISO date-time formatter 086 */ 087 public static DateTimeFormatter getISODateTimeFormatter() 088 { 089 return __ISO_OFFSET_DATE_TIME; 090 } 091 092 /** 093 * Return the readable name of a type 094 * 095 * @param type Type to convert 096 * @return Returns the name of the type 097 * @throws IllegalArgumentException If the type is unknwon 098 */ 099 public static String typeToString(ParameterType type) 100 { 101 return type.name().toLowerCase(); 102 } 103 104 /** 105 * Convert a string containing a type to its value 106 * 107 * @param type Name of the type 108 * @return Type 109 * @throws IllegalArgumentException if the type is unknown 110 */ 111 public static ParameterType stringToType(String type) 112 { 113 try 114 { 115 return ParameterType.valueOf(type.toUpperCase()); 116 } 117 catch (IllegalArgumentException e) 118 { 119 throw new IllegalArgumentException("The type '" + type + "' is unknown for config parameters"); 120 } 121 } 122 123 /** 124 * Cast a untyped value (string) to an object of the type 125 * 126 * @param value Value to cast 127 * @param type Type to cast value in 128 * @return An object of the type 'type' with value 'value', or null if type 129 * is unknown or value cannot be cast 130 */ 131 public static Object castValue(String value, ParameterType type) 132 { 133 134 if (value == null) 135 { 136 return null; 137 } 138 139 try 140 { 141 if (type == ParameterType.BOOLEAN) 142 { 143 return new Boolean(value); 144 } 145 else if (type == ParameterType.STRING) 146 { 147 return value; 148 } 149 else if (type == ParameterType.PASSWORD) 150 { 151 return value; 152 } 153 else if (type == ParameterType.DATASOURCE) 154 { 155 return value; 156 } 157 else if (type == ParameterType.LONG) 158 { 159 return new Long(value); 160 } 161 else if (type == ParameterType.DOUBLE) 162 { 163 return new Double(value); 164 } 165 else if (type == ParameterType.DATE) 166 { 167 LocalDateTime ldt = LocalDateTime.parse(value, DateTimeFormatter.ISO_DATE_TIME); 168 return DateUtils.asDate(ldt); 169 } 170 else if (type == ParameterType.BINARY) 171 { 172 return null; 173 } 174 } 175 catch (Exception nfe) 176 { 177 if (value.length() != 0) 178 { 179 _logger.error("Cannot cast value '" + value + "' into type '" + typeToString(type) + "'. Null object will be used.", nfe); 180 } 181 else if (_logger.isDebugEnabled()) 182 { 183 _logger.debug("Failed to cast empty string to type '" + typeToString(type) + "'. Null object will be used.", nfe); 184 } 185 } 186 return null; 187 } 188 189 190 /** 191 * Converts known types to string 192 * 193 * @param value Typed value 194 * @return String readable by the config bean 195 * @throws IllegalArgumentException if the object is a InputStream 196 */ 197 public static String valueToString(Object value) 198 { 199 if (value == null) 200 { 201 return null; 202 } 203 204 if (value instanceof Date) 205 { 206 ZonedDateTime zdt = DateUtils.asZonedDateTime((Date) value, null); 207 return zdt.format(getISODateTimeFormatter()); 208 } 209 210 if (value instanceof InputStream) 211 { 212 throw new IllegalArgumentException("The object to convert is an input stream"); 213 } 214 215 return value.toString(); 216 } 217 218 /** 219 * SAX a parameter 220 * @param handler The content handler where to SAX 221 * @param parameter The parameter to SAX 222 * @param value The parameter value. Can be null. 223 * @throws SAXException If an error occurred while SAXing 224 * @throws ProcessingException If an error occurred 225 */ 226 public static void toSAXParameter (ContentHandler handler, Parameter parameter, Object value) throws SAXException, ProcessingException 227 { 228 AttributesImpl parameterAttr = new AttributesImpl(); 229 parameterAttr.addAttribute("", "plugin", "plugin", "CDATA", parameter.getPluginName()); 230 XMLUtils.startElement(handler, parameter.getId(), parameterAttr); 231 232 toSAXParameterInternal(handler, parameter, value); 233 234 XMLUtils.endElement(handler, parameter.getId()); 235 } 236 237 /** 238 * SAX a parameter except the root tag 239 * @param handler The content handler where to SAX 240 * @param parameter The parameter to SAX 241 * @param value The parameter value. Can be null. 242 * @throws SAXException If an error occurred while SAXing 243 * @throws ProcessingException If an error occurred 244 */ 245 public static void toSAXParameterInternal(ContentHandler handler, Parameter parameter, Object value) throws SAXException, ProcessingException 246 { 247 parameter.getLabel().toSAX(handler, "label"); 248 parameter.getDescription().toSAX(handler, "description"); 249 250 XMLUtils.createElement(handler, "type", ParameterHelper.typeToString((ParameterType) parameter.getType())); 251 252 Object defaultValue = parameter.getDefaultValue(); 253 254 if (defaultValue != null) 255 { 256 XMLUtils.createElement(handler, "default-value", ParameterHelper.valueToString(defaultValue)); 257 } 258 259 if (value != null) 260 { 261 XMLUtils.createElement(handler, "value", ParameterHelper.valueToString(value)); 262 } 263 264 if (parameter.getWidget() != null) 265 { 266 XMLUtils.createElement(handler, "widget", parameter.getWidget()); 267 } 268 269 Map<String, I18nizableText> widgetParameters = parameter.getWidgetParameters(); 270 if (widgetParameters.size() > 0) 271 { 272 XMLUtils.startElement(handler, "widget-params"); 273 for (String paramName : widgetParameters.keySet()) 274 { 275 XMLUtils.startElement(handler, paramName); 276 widgetParameters.get(paramName).toSAX(handler); 277 XMLUtils.endElement(handler, paramName); 278 } 279 XMLUtils.endElement(handler, "widget-params"); 280 } 281 282 Enumerator enumerator = parameter.getEnumerator(); 283 if (enumerator != null) 284 { 285 toSAXEnumerator(handler, enumerator); 286 } 287 288 Validator validator = parameter.getValidator(); 289 toSAXValidator(handler, validator); 290 } 291 292 /** 293 * SAX parameter enumerator 294 * @param handler The content handler where to SAX 295 * @param enumerator The enumerator to SAX 296 * @throws SAXException If an error occurred to SAX 297 * @throws ProcessingException If an error occurred 298 */ 299 public static void toSAXEnumerator (ContentHandler handler, Enumerator enumerator) throws SAXException, ProcessingException 300 { 301 XMLUtils.startElement(handler, "enumeration"); 302 303 try 304 { 305 for (Map.Entry<Object, I18nizableText> entry : enumerator.getEntries().entrySet()) 306 { 307 String valueAsString = ParameterHelper.valueToString(entry.getKey()); 308 I18nizableText label = entry.getValue(); 309 310 // Generate option 311 AttributesImpl attrs = new AttributesImpl(); 312 attrs.addCDATAAttribute("value", valueAsString); 313 314 XMLUtils.startElement(handler, "option", attrs); 315 316 if (label != null) 317 { 318 label.toSAX(handler); 319 } 320 else 321 { 322 XMLUtils.data(handler, valueAsString); 323 } 324 325 XMLUtils.endElement(handler, "option"); 326 } 327 } 328 catch (Exception e) 329 { 330 throw new ProcessingException("Unable to enumerate entries with enumerator: " + enumerator, e); 331 } 332 333 XMLUtils.endElement(handler, "enumeration"); 334 } 335 336 /** 337 * SAX parameter validator 338 * @param handler The content handler where to SAX 339 * @param validator The validator to SAX 340 * @throws SAXException If an error occurred while SAXing 341 */ 342 public static void toSAXValidator (ContentHandler handler, Validator validator) throws SAXException 343 { 344 if (validator != null) 345 { 346 XMLUtils.startElement(handler, "validation"); 347 348 Map<String, Object> configuration = validator.getConfiguration(); 349 350 for (Map.Entry<String, Object> entry : configuration.entrySet()) 351 { 352 _saxConfigurationObject(handler, entry.getKey(), entry.getValue()); 353 } 354 355 XMLUtils.endElement(handler, "validation"); 356 } 357 } 358 359 @SuppressWarnings("unchecked") 360 private static void _saxConfigurationObject(ContentHandler handler, String name, Object value) throws SAXException 361 { 362 if (value instanceof I18nizableText) 363 { 364 ((I18nizableText) value).toSAX(handler, name); 365 } 366 else if (value instanceof Collection) 367 { 368 for (Object item : (Collection) value) 369 { 370 if (item != null) 371 { 372 _saxConfigurationObject(handler, name, item); 373 } 374 } 375 } 376 else if (value instanceof Map) 377 { 378 XMLUtils.startElement(handler, name); 379 for (Map.Entry<String, Object> subEntry : ((Map<String, Object>) value).entrySet()) 380 { 381 _saxConfigurationObject(handler, subEntry.getKey(), subEntry.getValue()); 382 } 383 XMLUtils.endElement(handler, name); 384 } 385 else if (value instanceof Object[]) 386 { 387 for (Object item : (Object[]) value) 388 { 389 if (item != null) 390 { 391 _saxConfigurationObject(handler, name, item); 392 } 393 } 394 } 395 else 396 { 397 XMLUtils.createElement(handler, name, String.valueOf(value)); 398 } 399 } 400 401 /** 402 * Convert the parameter in a JSON map 403 * @param parameter The parameter to convert 404 * @return The Parameter as a map 405 * @throws ProcessingException If an error occured when converting the parameter 406 */ 407 public static Map<String, Object> toJSON(Parameter parameter) throws ProcessingException 408 { 409 Map<String, Object> result = new HashMap<>(); 410 411 result.put("id", parameter.getId()); 412 result.put("label", parameter.getLabel()); 413 result.put("description", parameter.getDescription()); 414 result.put("plugin", parameter.getPluginName()); 415 result.put("type", parameter.getType().name()); 416 417 if (parameter.getDefaultValue() != null) 418 { 419 result.put("default-value", parameter.getDefaultValue()); 420 } 421 422 if (parameter.getValidator() != null) 423 { 424 result.put("validation", parameter.getValidator().getConfiguration()); 425 } 426 427 if (parameter.getEnumerator() != null) 428 { 429 Enumerator enumerator = parameter.getEnumerator(); 430 List<Map<String, Object>> enumeration = new ArrayList<>(); 431 432 try 433 { 434 Map<Object, I18nizableText> entries = enumerator.getEntries(); 435 for (Object entryKey : entries.keySet()) 436 { 437 Map<String, Object> option = new HashMap<>(); 438 option.put("value", ParameterHelper.valueToString(entryKey)); 439 option.put("label", entries.get(entryKey)); 440 enumeration.add(option); 441 } 442 } 443 catch (Exception e) 444 { 445 throw new ProcessingException("Unable to enumerate entries with enumerator: " + enumerator, e); 446 } 447 448 result.put("enumeration", enumeration); 449 result.put("enumerationConfig", enumerator.getConfiguration()); 450 } 451 452 if (parameter.getWidget() != null) 453 { 454 result.put("widget", parameter.getWidget()); 455 } 456 457 Map widgetParameters = parameter.getWidgetParameters(); 458 if (widgetParameters != null && !widgetParameters.isEmpty()) 459 { 460 result.put("widget-params", parameter.getWidgetParameters()); 461 } 462 463 return result; 464 } 465}